本功能在 Swagger 2.7.0 和 2.9.2 版本 测试通过 ,保证可正常使用,
有问题联系[email protected]
关于Swagger2本身不支持Map/JSONObject传参 生成文档,在网上找了很多,大多是自定义注解,借助第三方插件动态生成类,或者重写Swagger2的多数文件,显得很复杂,而且要看很多代码,跟踪才能了解并修改为自己的,而且还时好时坏,但是这些资料又给我带来了新的灵感。
大体思路,
1、新建一个静态Map,存储自己新建的model变量(不需要创建实体类)
2、自定义注解,实现配置参数信息
3、继承Swagger的部分builder类,根据注解 创建自己的 model 塞入 静态Map
4、添加Swagger Aop切面,将静态Map 合并到 Swagger的model中返回
下面是实现代码
import com.cloud.utils.util.baseenum.DataType;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author: fengyuan
* @date 2021-6-3 17:23:28
* @Description TODO Swagger2配置
*/
@Target(ElementType.ANNOTATION_TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface PropFy2 {
String name(); // key
String eg() default "";// 示例
DataType type() default DataType.String; // 支持string、int、double
String desc() default "";// 参数描述
boolean required() default false; // 是否必传
}
import com.cloud.utils.util.baseenum.DataType;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author: fengyuan
* @date 2021-6-3 17:23:28
* @Description TODO Swagger2配置
*/
@Target(ElementType.ANNOTATION_TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface PropFy {
String name(); // key
String eg() default "";// 示例
DataType type() default DataType.String; // 支持string、int、double
String desc() default "";// 参数描述
boolean required() default false; // 是否必传
PropFy2[] map() default {};
}
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author: fengyuan
* @date 2021-6-3 17:23:28
* @Description TODO Swagger2配置
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ResObjFY {
PropFy[] value() default {}; //请求属性值
}
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author: fengyuan
* @date 2021-6-3 17:23:28
* @Description TODO Swagger2配置
*/
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface ReqObjFy {
PropFy[] value(); //对象属性值
}
//随便找个地方
public static Map<String, Model> modelMap = new HashMap<>();
//此为创建Model的方法,深度支持2级
import com.cloud.utils.util.baseenum.DataType;
import com.cloud.utils.utilabstract.BasePubFun;
import io.swagger.models.ModelImpl;
import io.swagger.models.properties.*;
import org.apache.commons.lang3.StringUtils;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* @author: fengyuan
* @date 2021-6-3 15:17:49
* @Description TODO Swagger2配置公共方法
*/
public class SwaggerFun {
public static String createModelRst(String name, PropFy[] pros) {
createModel(name, pros);
Map<String, Property> proMap = new LinkedHashMap<>();
Property code = new StringProperty();
code.setName("code");
code.setDescription("状态'10000'成功,其他失败");
code.setExample((Object) "10000");
proMap.put("code", code);
Property msg = new StringProperty();
msg.setName("msg");
msg.setDescription("提示");
proMap.put("msg", msg);
Property msgext = new StringProperty();
msgext.setName("msgext");
msgext.setDescription("详情");
proMap.put("msgext", msgext);
Property result = new RefProperty(name);
result.setName("result");
result.setDescription("结果集");
proMap.put("result", result);
Property success = new BooleanProperty();
success.setName("success");
success.setDescription("状态 true成功 false失败");
proMap.put("success", success);
//创建model
String rstName = BasePubFun.format("RstData<{}>", name);
ModelImpl model = new ModelImpl();
model.setName(rstName);
model.setProperties(proMap);
model.setType("object");
SwaggerFy.modelMap.put(rstName, model);
return model.getName();
}
public static void createModel(String name, PropFy[] pros) {
Map<String, Property> proMap = new LinkedHashMap<>();
for (PropFy pro : pros) {
Property property = null;
try {
if (DataType.String.equals(pro.type())) {
property = new StringProperty();
property.setExample(StringUtils.isBlank(pro.eg()) ? "" : (Object) pro.eg());
} else if (DataType.Long.equals(pro.type())) {
property = new LongProperty();
property.setExample(StringUtils.isBlank(pro.eg()) ? 0L : Long.valueOf(pro.eg()));
} else if (DataType.Integer.equals(pro.type())) {
property = new IntegerProperty();
property.setExample(StringUtils.isBlank(pro.eg()) ? 0 : Integer.valueOf(pro.eg()));
} else if (DataType.Date.equals(pro.type())) {
property = new DateProperty();
property.setExample(StringUtils.isBlank(pro.eg()) ? "" : Boolean.valueOf(pro.eg()));
} else if (DataType.Boolean.equals(pro.type())) {
property = new BooleanProperty();
property.setExample(StringUtils.isBlank(pro.eg()) ? "" : Boolean.valueOf(pro.eg()));
} else if (DataType.Map.equals(pro.type())) {
String tempName = name + "_" + pro.name();
createModel(tempName, pro.map());
property = new RefProperty(tempName);
} else {
property = new StringProperty();
property.setExample((Object) pro.eg());
}
} catch (Exception e) {
}
property.setDescription(pro.desc());
property.setRequired(pro.required());
property.setName(pro.name());
proMap.put(pro.name(), property);
}
ModelImpl model = new ModelImpl();
model.setName(name);
model.setProperties(proMap);
model.setType("object");
SwaggerFy.modelMap.put(name, model);
}
private static void createModel(String name, PropFy2[] pros) {
Map<String, Property> proMap = new LinkedHashMap<>();
for (PropFy2 pro : pros) {
Property property = null;
if (DataType.String.equals(pro.type())) {
property = new StringProperty();
property.setExample(StringUtils.isBlank(pro.eg()) ? "" : (Object) pro.eg());
} else if (DataType.Long.equals(pro.type())) {
property = new LongProperty();
property.setExample(StringUtils.isBlank(pro.eg()) ? 0L : Long.valueOf(pro.eg()));
} else if (DataType.Integer.equals(pro.type())) {
property = new IntegerProperty();
property.setExample(StringUtils.isBlank(pro.eg()) ? 0 : Integer.valueOf(pro.eg()));
} else if (DataType.Date.equals(pro.type())) {
property = new DateProperty();
property.setExample(StringUtils.isBlank(pro.eg()) ? "" : Boolean.valueOf(pro.eg()));
} else if (DataType.Boolean.equals(pro.type())) {
property = new BooleanProperty();
property.setExample(!StringUtils.isBlank(pro.eg()) && Boolean.valueOf(pro.eg()));
}
property.setDescription(pro.desc());
property.setRequired(pro.required());
property.setName(pro.name());
proMap.put(pro.name(), property);
}
ModelImpl model = new ModelImpl();
model.setName(name);
model.setProperties(proMap);
model.setType("object");
SwaggerFy.modelMap.put(name, model);
}
}
import com.alibaba.fastjson.JSONObject;
import com.google.common.base.Optional;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.ResolvedMethodParameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.ParameterBuilderPlugin;
import springfox.documentation.spi.service.contexts.ParameterContext;
import java.util.Map;
/**
* @author: fengyuan
* @date 2021-6-3 15:17:49
* @Description TODO Swagger2配置
*/
@Component
@Order // plugin加载顺序,默认是最后加载
public class ParamReqPlugin implements ParameterBuilderPlugin {
@Override
public void apply(ParameterContext parameterContext) {
ResolvedMethodParameter methodParameter = parameterContext.resolvedMethodParameter();
// 判断是否需要修改对象ModelRef,这里我判断的是Map类型和JSONObject类型需要重新修改ModelRef对象
if (!methodParameter.getParameterType().canCreateSubtype(Map.class) && methodParameter.getParameterType().canCreateSubtype(JSONObject.class)) {
return;
}
Optional<ReqObjFy> optional = methodParameter.findAnnotation(ReqObjFy.class);
if (!optional.isPresent()) {
return;
}
String name = parameterContext.getOperationContext().requestMappingPattern().replace("/", "_").substring(1);
SwaggerFun.createModel(name, optional.get().value());
parameterContext.parameterBuilder().parameterType("body").modelRef(new ModelRef(name)).name(name);
}
@Override
public boolean supports(DocumentationType delimiter) {
return true;
}
}
import com.cloud.utils.dto.RstData;
import com.fasterxml.classmate.ResolvedType;
import com.google.common.base.Optional;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import springfox.documentation.builders.ResponseMessageBuilder;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.ResponseMessage;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.OperationBuilderPlugin;
import springfox.documentation.spi.service.contexts.OperationContext;
import java.util.HashSet;
/**
* @author: fengyuan
* @date 2021-6-3 15:17:49
* @Description TODO Swagger2配置
*/
@Component
@Order // plugin加载顺序,默认是最后加载
public class ParamResPlugin implements OperationBuilderPlugin {
@Override
public void apply(OperationContext operationContext) {
ResolvedType returnType = operationContext.getReturnType();
// 判断是否需要修改对象ModelRef,这里我判断的是RstData类型需要重新修改ModelRef对象
if (!returnType.canCreateSubtype(RstData.class)) {
return;
}
Optional<ResObjFY> optional = operationContext.findAnnotation(ResObjFY.class);
if (!optional.isPresent()) {
return;
}
String name = operationContext.requestMappingPattern().replace("/", "_").substring(1) + "_rst";
String rstName = SwaggerFun.createModelRst(name, optional.get().value());
ResponseMessage responseMessage = new ResponseMessageBuilder()
.code(200).responseModel(new ModelRef(rstName))
.build();
HashSet<ResponseMessage> responseMessages = new HashSet<>();
responseMessages.add(responseMessage);
operationContext.operationBuilder().responseMessages(responseMessages);
}
@Override
public boolean supports(DocumentationType delimiter) {
return true;
}
}
import io.swagger.models.Swagger;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
/**
* @author: fengyuan
* @date 2021-6-3 17:23:28
* @Description TODO Swagger2配置
*/
@Aspect
@Component
public class SwaggerAop {
@Pointcut(value = "execution(public * springfox.documentation.swagger2.mappers.ServiceModelToSwagger2MapperImpl.mapDocumentation(..))")
public void point() {
}
@Around("point()")
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
Swagger swagger = (Swagger) proceedingJoinPoint.proceed();
swagger.getDefinitions().putAll(SwaggerFy.modelMap);
return swagger;
}
}
@PostMapping(value = "demo")
@ApiOperation("请求参数配置方式,支持二级")
@ResObjFY({
@PropFy(name = "id", eg = "123", desc = "主键", type = DataType.Long, required = true),
@PropFy(name = "row", desc = "具体数据", type = DataType.Map, map = {
@PropFy2(name = "idCard", eg = "610126198911111111", desc = "身份证号", type = DataType.String, required = true),
@PropFy2(name = "name", eg = "张三", desc = "姓名", required = true),
@PropFy2(name = "sex", eg = "1", desc = "性别 1男 2女", type = DataType.Integer),
})
})
public RstData demo(
@ReqObjFy({
@PropFy(name = "id", eg = "123", desc = "主键", type = DataType.Long, required = true),
@PropFy(name = "row", desc = "具体数据", type = DataType.Map, map = {
@PropFy2(name = "idCard", eg = "610126198911111111", desc = "身份证号", type = DataType.String, required = true),
@PropFy2(name = "name", eg = "张三", desc = "姓名", required = true),
@PropFy2(name = "sex", eg = "1", desc = "性别 1男 2女", type = DataType.Integer),
})
})
@RequestBody Map<String, Object> params) {
return RstData.success(params);
}
效果图
2.7.0
2.9.2
下面为Swagger扩展Ui的依赖及效果图 <dependency> <groupId>com.github.xiaoymin</groupId> <artifactId>swagger-bootstrap-ui</artifactId> <version>1.9.6</version> </dependency>