当前位置:   article > 正文

Swagger2 JSON入参使用Map、JSONObject等非实体类接收时的处理_swagger2 jsonobject

swagger2 jsonobject

Swagger2 JSON入参使用Map、JSONObject等非实体类接收时的处理,基本就是扩展swagger插件通过注解动态生成实体类。
以下提供3种实现,可以按需选择:

一. ApiGlobalModel

ApiGlobalModel注解用于从一个已有的实体类中抽取接口所需的参数字段
示例:

    /**
     * 修改地址 - ApiGlobalModel
     *
     * @return R
     */
    @PostMapping("2")
    @ApiOperation(value = "修改地址 - ApiGlobalModel")
    @ApiGlobalModel(component = GlobalVo.class, value = "addressId,telNumber")
    public Resp<AddressVo> updateByApiGlobalModel(JSONObject param) {
        //***************************** 读取参数并校验 *******************************/
        Integer id = param.getInteger("id");
        String telNumber = param.getString("telNumber");
        //***************************** 处理业务并返回 *******************************/
        return Resp.success(null);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
package com.platform.po;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import java.io.Serializable;

/**
 * 出参字段集合类 用于ApiGlobalModel
 * 不可继承 不可实例化 只是字段描述文件
 *
 */
@Data
@ApiModel
public abstract class GlobalVo implements Serializable {
    private GlobalVo() {
    }

    @ApiModelProperty(value = "收货地址id", name = "id")
    private Integer addressId;

    @ApiModelProperty(value = "收件人电话")
    private Integer telNumber;

    @ApiModelProperty(value = "购物车id", name = "id")
    private Integer cartId;

    @ApiModelProperty("商品Id")
    private Integer goodsId;

    @ApiModelProperty("sku_id")
    private Integer skuId;

    @ApiModelProperty("数量")
    private Integer number;

    @ApiModelProperty("已选规格列表")
    private String properties;

    @ApiModelProperty(name = "id", value = "用户id")
    private String userId;

    @ApiModelProperty(name = "id", value = "订单id")
    private String orderId;

    @ApiModelProperty("用户名")
    private String userName;

    @ApiModelProperty("openId")
    private String openId;

    @ApiModelProperty("手机号码")
    private String mobile;

    @ApiModelProperty("短信验证码")
    private String smsCode;

    @ApiModelProperty("短信验证码Key")
    private String smsCodeKey;

    @ApiModelProperty("图形验证码Key")
    private String captchaKey;

    @ApiModelProperty("图形验证码")
    private String captchaCode;

    @ApiModelProperty("密码")
    private String password;

    @ApiModelProperty(value = "新密码", name = "password")
    private String newPassword;

    @ApiModelProperty("微信code")
    private String code;

    @ApiModelProperty(name = "code", value = "支付宝code")
    private String aliCode;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79

效果:
这里使用了knife4j来美化swagger,方便查看和调试
在这里插入图片描述
以上,GlobalVo类用于存放接口入参字段,这是一个不可实现不可继承的实体类,仅用于字段描述
可以在这个类里定义所有需要的字段,相同的参数可以复用
描述这个实体类使用的是swagger原生注解

另一个使用场景比如有个修改订单状态的接口,虽然我们有订单实体类GoodsOrderEntity.java,但这个接口只需要订单id和订单状态两个字段,如果把整个订单实体类丢给swagger,接口文档就会显示一大堆不必要的参数,这显然不是前端需要的,这时候就可以使用ApiGlobalModel抽取仅需要的字段

    /**
     * 修改订单状态 - ApiGlobalModel
     *
     * @return R
     */
    @PostMapping("3")
    @ApiOperation(value = "修改订单状态 - ApiGlobalModel")
    @ApiGlobalModel(component = GoodsOrderEntity.class, value = "id,orderStatus")
    public Resp<Object> changeOrderStatus(JSONObject param) {
        //***************************** 读取参数并校验 *******************************/
        Integer id = param.getInteger("id");
        Integer orderStatus = param.getInteger("orderStatus");
        //***************************** 处理业务并返回 *******************************/
        return Resp.success(null);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

在这里插入图片描述
以上,GoodsOrderEntity是一个用swagger注解正常描述的实体类,字段非常多,我们抽离出需要的放到接口文档里。

ApiGlobalModel方式需要以下两个类的支持:ApiGlobalModel.java和ApiGlobalModelBuilder.java

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * Swagger扩展注解
 * 用于application/json请求
 * 并使用诸如Map或JSONObject等非具体实体类接收参数时,对参数进行进一步描述
 */
@Target({ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiGlobalModel {
    /**
     * 字段集合容器
     *
     * @return Global model
     */
    Class<?> component();

    /**
     * 分隔符
     *
     * @return separator
     */
    String separator() default ",";

    /**
     * 实际用到的字段
     * 可以是字符串数组,也可以是一个字符串 多个字段以分隔符隔开: "id,name"
     * 注意这里对应的是component里的属性名,但swagger显示的字段名实际是属性注解上的name
     *
     * @return value
     */
    String[] value() default {};
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
import com.fasterxml.classmate.TypeResolver;
import com.google.common.base.Optional;
import io.swagger.annotations.ApiModelProperty;
import javassist.*;
import javassist.bytecode.AnnotationsAttribute;
import javassist.bytecode.annotation.Annotation;
import javassist.bytecode.annotation.MemberValue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.ParameterBuilderPlugin;
import springfox.documentation.spi.service.contexts.ParameterContext;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * ApiJsonParameterBuilder
 * <p>
 * 将map入参匹配到swagger文档的工具类
 * plugin加载顺序,默认是最后加载
 *
 */
@Component
@Order
public class ApiGlobalModelBuilder implements ParameterBuilderPlugin {
    private static final Logger logger = LoggerFactory.getLogger(ApiGlobalModelBuilder.class);
    @Autowired
    private TypeResolver typeResolver;
    /**
     * 动态生成的Class的包路径 自由定义
     */
    private final static String BASE_PACKAGE = "com.platform.entity.";
    /**
     * 默认类名
     */
    private final static String DEFAULT_CLASS_NAME = "RequestBody";
    /**
     * 序号 防止重名
     */
    private static Integer i = 0;

    @Override
    public void apply(ParameterContext context) {
        try {
            // 从方法或参数上获取指定注解的Optional
            Optional<ApiGlobalModel> optional = context.getOperationContext().findAnnotation(ApiGlobalModel.class);
            if (!optional.isPresent()) {
                optional = context.resolvedMethodParameter().findAnnotation(ApiGlobalModel.class);
            }
            if (optional.isPresent()) {
                String key = DEFAULT_CLASS_NAME + i++;
                ApiGlobalModel apiAnno = optional.get();
                try {
                    //类名重复将导致swagger识别不准确 主动触发异常
                    Class.forName(BASE_PACKAGE + key);
                } catch (ClassNotFoundException e) {
                    String[] fields = apiAnno.value();
                    String separator = apiAnno.separator();
                    ClassPool pool = ClassPool.getDefault();
                    CtClass ctClass = pool.makeClass(BASE_PACKAGE + key);
                    ctClass.setModifiers(Modifier.ABSTRACT);
                    //处理 javassist.NotFoundException
                    pool.insertClassPath(new ClassClassPath(apiAnno.component()));
                    CtClass globalCtClass = pool.getCtClass(apiAnno.component().getName());

                    //从globalCtClass拷贝指定字段到动态创建的类中
                    for (String field : merge(fields, separator)) {
                        //若指定的字段不存在 throw NotFoundException
                        CtField ctField = globalCtClass.getDeclaredField(field);
                        CtField newCtField = new CtField(ctField, ctClass);
                        handleField(newCtField);
                        ctClass.addField(newCtField);
                    }
                    // 将生成的Class添加到SwaggerModels
                    context.getDocumentationContext().getAdditionalModels()
                            .add(typeResolver.resolve(ctClass.toClass()));
                    // 修改Json参数的ModelRef为动态生成的class
                    context.parameterBuilder()
                            .parameterType("body").modelRef(new ModelRef(key)).name("body").description("body");
                }
            }
        } catch (Exception e) {
            logger.error("@ApiGlobalModel Error", e);
        }
    }

    private void handleField(CtField field) {
        //防止private又没有getter
        field.setModifiers(Modifier.PUBLIC);
        //有name的把字段名改为name
        //因为JSON格式化的原因,ApiModelProperty的name属性无效 所以如果有name,直接更改字段名为name
        AnnotationsAttribute annos = ((AnnotationsAttribute) field.getFieldInfo().getAttribute("RuntimeVisibleAnnotations"));
        if (annos != null) {
            Annotation anno = annos.getAnnotation(ApiModelProperty.class.getTypeName());
            if (anno != null) {
                MemberValue name = anno.getMemberValue("name");
                if (name != null) {
                    //这里返回的name会以引号包裹
                    String fName = name.toString().replace("\"", "").trim();
                    if (fName.length() > 0) {
                        field.setName(fName);
                    }
                }
            }
        }

    }

    /**
     * 字符串列表 分隔符 合并
     * A{"a","b,c","d"} => B{"a","d","b","c"}
     *
     * @param arr arr
     * @return list
     */
    private List<String> merge(String[] arr, String separator) {
        List<String> tmp = new ArrayList<>();
        Arrays.stream(arr).forEach(s -> {
            if (s.contains(separator)) {
                tmp.addAll(Arrays.asList(s.split(separator)));
            } else {
                tmp.add(s);
            }
        });
        return tmp;
    }

    @Override
    public boolean supports(DocumentationType delimiter) {
        return true;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138

二. ApiJsonModel

使用ApiJsonModel注解不需要预先定义实体类,所有描述都在接口方法的注解上,描述具体字段的时候使用的依然是swagger原生注解ApiModelProperty
优点是不需要创建实体类,缺点是很多注解在方法上比较难看,另外字段描述不可复用
示例:

    /**
     * 修改地址 - ApiJsonModel
     *
     * @return R
     */
    @PostMapping("4")
    @ApiOperation(value = "修改地址 - ApiJsonModel")
    @ApiJsonModel({
            @ApiModelProperty(name = "id", value = "收件人电话"),
            @ApiModelProperty(name = "telNumber", value = "收货地址id")
    })
    public Resp<AddressVo> saveByApiJsonModel(JSONObject param) {
        //***************************** 读取参数并校验 *******************************/
        Integer id = param.getInteger("id");
        String telNumber = param.getString("telNumber");
        //***************************** 处理业务并返回 *******************************/
        return Resp.success(null);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

在这里插入图片描述
ApiJsonModel方式需要以下两个类的支持:ApiJsonModel.java和ApiJsonModelBuilder.java

import io.swagger.annotations.ApiModelProperty;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * Swagger扩展注解
 * 用于application/json请求
 * 并使用诸如Map或JSONObject等非具体实体类接收参数时,对参数进行进一步描述
 *
 */
@Target({ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiJsonModel {
    /**
     * 原生Swagger注解 用于描述具体字段
     * accessMode,extensions配置将无效
     *
     * @return value
     */
    ApiModelProperty[] value() default {};
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
import com.fasterxml.classmate.TypeResolver;
import com.google.common.base.Optional;
import io.swagger.annotations.ApiModelProperty;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtField;
import javassist.Modifier;
import javassist.bytecode.AnnotationsAttribute;
import javassist.bytecode.ConstPool;
import javassist.bytecode.annotation.Annotation;
import javassist.bytecode.annotation.BooleanMemberValue;
import javassist.bytecode.annotation.IntegerMemberValue;
import javassist.bytecode.annotation.StringMemberValue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.ParameterBuilderPlugin;
import springfox.documentation.spi.service.contexts.ParameterContext;

import java.lang.reflect.Method;
import java.lang.reflect.Type;

/**
 * ApiJsonParameterBuilder
 * <p>
 * 将map入参匹配到swagger文档的工具类
 * plugin加载顺序,默认是最后加载
 *
 */
@Component
@Order
public class ApiJsonModelBuilder implements ParameterBuilderPlugin {
    private static final Logger logger = LoggerFactory.getLogger(ApiJsonModelBuilder.class);
    @Autowired
    private TypeResolver typeResolver;
    /**
     * 动态生成的Class的包路径 自由定义
     */
    private final static String BASE_PACKAGE = "com.platform.entity.";
    /**
     * 默认类名
     */
    private final static String DEFAULT_CLASS_NAME = "RequestData";
    /**
     * 序号 防止重名
     */
    private static Integer i = 0;

    @Override
    public void apply(ParameterContext context) {
        try {
            // 从方法或参数上获取指定注解的Optional
            Optional<ApiJsonModel> optional = context.getOperationContext().findAnnotation(ApiJsonModel.class);
            if (!optional.isPresent()) {
                optional = context.resolvedMethodParameter().findAnnotation(ApiJsonModel.class);
            }
            if (optional.isPresent()) {
                String key = DEFAULT_CLASS_NAME + i++;
                ApiJsonModel apiAnno = optional.get();
                try {
                    Class.forName(BASE_PACKAGE + key);
                } catch (ClassNotFoundException e) {
                    ApiModelProperty[] properties = apiAnno.value();
                    // 将生成的Class添加到SwaggerModels
                    context.getDocumentationContext().getAdditionalModels()
                            .add(typeResolver.resolve(createRefModel(properties, key)));
                    // 修改Json参数的ModelRef为动态生成的class
                    context.parameterBuilder()
                            .parameterType("body").modelRef(new ModelRef(key)).name("body").description("body");
                }
            }
        } catch (Exception e) {
            logger.error("@ApiJsonModel Error", e);
        }
    }

    /**
     * MapReaderForApi
     * 根据propertys中的值动态生成含有Swagger注解的javaBeen
     *
     * @author chengz
     * @date 2020/10/14
     */
    private Class<?> createRefModel(ApiModelProperty[] propertys, String key) throws Exception {
        ClassPool pool = ClassPool.getDefault();
        CtClass ctClass = pool.makeClass(BASE_PACKAGE + key);
        ctClass.setModifiers(Modifier.ABSTRACT);
        for (ApiModelProperty property : propertys) {
            ctClass.addField(createField(property, ctClass));
        }
        return ctClass.toClass();
    }

    /**
     * 根据property的值生成含有swagger apiModelProperty注解的属性
     */
    private CtField createField(ApiModelProperty property, CtClass ctClass) throws Exception {
        //此处默认字段类型为String 如果不是 swagger也是取注解的dataType 字段类型就不重要了
        CtField ctField = new CtField(ClassPool.getDefault().get(String.class.getName()), property.name(), ctClass);
        ctField.setModifiers(Modifier.PUBLIC);
        ConstPool constPool = ctClass.getClassFile().getConstPool();
        AnnotationsAttribute attr = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag);
        //不知道是否有直接把property转为对应Annotation的方法 它们本质是一样的
        Annotation anno = new Annotation(ApiModelProperty.class.getTypeName(), constPool);
        Method[] members = ApiModelProperty.class.getDeclaredMethods();
        for (Method member : members) {
            Object value = member.invoke(property);
            //使用默认值的就不重复赋值了
            if (!value.equals(member.getDefaultValue())) {
                //由于只拷贝了String,int,boolean等返回值的属性,所以诸如accessMode,extensions这样的属性设置将无效
                Type type = member.getReturnType();
                if (type == String.class) {
                    anno.addMemberValue(member.getName(), new StringMemberValue(String.valueOf(member.invoke(property)), constPool));
                } else if (type == int.class) {
                    anno.addMemberValue(member.getName(), new IntegerMemberValue((Integer) member.invoke(property), constPool));
                } else if (type == boolean.class) {
                    anno.addMemberValue(member.getName(), new BooleanMemberValue((Boolean) member.invoke(property), constPool));
                }
            }
        }
        attr.addAnnotation(anno);
        ctField.getFieldInfo().addAttribute(attr);
        return ctField;
    }

    @Override
    public boolean supports(DocumentationType delimiter) {
        return true;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134

三. ApiSimpleModel

ApiSimpleModel注解只用于定义字段名,字段类型默认Strng,用于快速定义简单接口
示例:

    /**
     * 修改地址 - ApiSimpleModel
     *
     * @return R
     */
    @PostMapping("5")
    @ApiSimpleModel("id,telNumber")
    @ApiOperation(value = "修改地址 - ApiSimpleModel")
    public Resp<AddressVo> saveByApiSimpleModel(JSONObject param) {
        //***************************** 读取参数并校验 *******************************/
        Integer id = param.getInteger("id");
        String telNumber = param.getString("telNumber");
        //***************************** 处理业务并返回 *******************************/
        return Resp.success(null);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

在这里插入图片描述
ApiSimpleModel方式需要以下两个类的支持:ApiSimpleModel.java和ApiSimpleModelBuilder.java

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * Swagger扩展注解
 * 用于application/json请求
 * 并使用诸如Map或JSONObject等非具体实体类接收参数时,对参数进行进一步描述
 * 简易描述 只有参数名
 *
 */
@Target({ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiSimpleModel {
    /**
     * 分隔符
     *
     * @return separator
     */
    String separator() default ",";

    /**
     * 参数列表
     * 可以是字符串数组,也可以是一个字符串 多个字段以分隔符隔开: "id,name"
     *
     * @return value
     */
    String[] value() default {};
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
import com.fasterxml.classmate.TypeResolver;
import com.google.common.base.Optional;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtField;
import javassist.Modifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.ParameterBuilderPlugin;
import springfox.documentation.spi.service.contexts.ParameterContext;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * ApiJsonParameterBuilder
 * <p>
 * 将map入参匹配到swagger文档的工具类
 * plugin加载顺序,默认是最后加载
 *
 */
@Component
@Order
public class ApiSimpleModelBuilder implements ParameterBuilderPlugin {
    private static final Logger logger = LoggerFactory.getLogger(ApiSimpleModelBuilder.class);
    @Autowired
    private TypeResolver typeResolver;
    /**
     * 动态生成的Class的包路径 自由定义
     */
    private final static String BASE_PACKAGE = "com.platform.entity.";
    /**
     * 默认类名
     */
    private final static String DEFAULT_CLASS_NAME = "RequestInfo";
    /**
     * 序号 防止重名
     */
    private static Integer i = 0;

    @Override
    public void apply(ParameterContext context) {
        try {
            // 从方法或参数上获取指定注解的Optional
            Optional<ApiSimpleModel> optional = context.getOperationContext().findAnnotation(ApiSimpleModel.class);
            if (!optional.isPresent()) {
                optional = context.resolvedMethodParameter().findAnnotation(ApiSimpleModel.class);
            }
            if (optional.isPresent()) {
                String key = DEFAULT_CLASS_NAME + i++;
                ApiSimpleModel apiAnno = optional.get();
                try {
                    //类名重复将导致swagger识别不准确 主动触发异常
                    Class.forName(BASE_PACKAGE + key);
                } catch (ClassNotFoundException e) {
                    String[] fields = apiAnno.value();
                    String separator = apiAnno.separator();
                    List<String> fieldList = merge(fields, separator);
                    Class<?> ctClass = createRefModel(fieldList, key);
                    // 将生成的Class添加到SwaggerModels
                    context.getDocumentationContext().getAdditionalModels()
                            .add(typeResolver.resolve(ctClass));
                    // 修改Json参数的ModelRef为动态生成的class
                    context.parameterBuilder()
                            .parameterType("body").modelRef(new ModelRef(key)).name("body").description("body");
                }
            }
        } catch (Exception e) {
            logger.error("@ApiSimpleModel Error", e);
        }
    }

    /**
     * 字符串列表 分隔符 合并
     * A{"a","b,c","d"} => B{"a","d","b","c"}
     *
     * @param arr arr
     * @return list
     */
    private List<String> merge(String[] arr, String separator) {
        List<String> tmp = new ArrayList<>();
        Arrays.stream(arr).forEach(s -> {
            if (s.contains(separator)) {
                tmp.addAll(Arrays.asList(s.split(separator)));
            } else {
                tmp.add(s);
            }
        });
        return tmp;
    }

    /**
     * MapReaderForApi
     * 根据propertys中的值动态生成含有Swagger注解的javaBeen
     *
     * @author chengz
     * @date 2020/10/14
     */
    private Class<?> createRefModel(List<String> fieldList, String key) throws Exception {
        ClassPool pool = ClassPool.getDefault();
        CtClass ctClass = pool.makeClass(BASE_PACKAGE + key);
        ctClass.setModifiers(Modifier.ABSTRACT);
        for (String field : fieldList) {
            CtField newCtField = createField(field, ctClass);
            ctClass.addField(newCtField);
        }
        return ctClass.toClass();
    }

    /**
     * 根据property的值生成含有swagger apiModelProperty注解的属性
     */
    private CtField createField(String field, CtClass ctClass) throws Exception {
        CtField ctField = new CtField(ClassPool.getDefault().get(String.class.getName()), field, ctClass);
        ctField.setModifiers(Modifier.PUBLIC);
        return ctField;
    }

    @Override
    public boolean supports(DocumentationType delimiter) {
        return true;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129

四. 使用示例

==>使用示例

如果swagger文档里只显示body不显示字段的加这个controller到项目里:

import io.swagger.annotations.Api;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 如果系统最后一个加载的接口加了@ApiGlobalModel/@ApiJsonModel/@ApiSimpleModel,这个接口动态生成的参数在swagger文档里将找不到
 * 出现这种情况就加上这个Controller,保证这个Controller是最后一个Controller => 保证最后一个接口没有使用以上3个注解
 */
@RestController
@RequestMapping("/whatever")
@Api(tags = "whatever")
public class ZZZController {
    @GetMapping("/whatever")
    public void whatever() {
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

参考:https://blog.csdn.net/hellopeng1/article/details/82227942

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/人工智能uu/article/detail/897694
推荐阅读
相关标签
  

闽ICP备14008679号