背景:
最近接到老大的一個任務,用一個DTO適用於多個接口,通過某個接口上註解解決把不需要得屬性再swagger上隱藏掉。
結果:
一個DTO適用於2個接口
代碼
步驟一
1.引入pom
<!-- swagger2 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
2.springBoot 開啓
@SpringBootApplication
@EnableAsync
@EnableSwagger2 //開啓swagger
public class RenrenApplication {
public static void main(String[] args) {
SpringApplication.run(RenrenApplication.class, args);
}
}
步驟二
1.自定義註解
@Target({ElementType.PARAMETER, ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiIgp {
String[] value(); //對象屬性值
}
2. 實現parameterBuilderPlugin
ps: 注意這裏用的是alibaba 的javassist 的包最好
@Component
@Order //plugin加載順序,默認是最後加載
public class MapApiReader implements ParameterBuilderPlugin {
@Autowired
private TypeResolver typeResolver;
@Override
public void apply(ParameterContext parameterContext) {
ResolvedMethodParameter methodParameter = parameterContext.resolvedMethodParameter();
Class originClass = parameterContext.resolvedMethodParameter().getParameterType().getErasedType();
Optional<ApiIgp> optional = methodParameter.findAnnotation(ApiIgp.class);
if (optional.isPresent()) {
Random random = new Random();
String name = originClass.getSimpleName() + "Model" + random.nextInt(100); //model 名稱
String[] properties = optional.get().value();
try {
parameterContext.getDocumentationContext()
.getAdditionalModels()
.add(typeResolver.resolve(createRefModelIgp(properties, originClass.getPackage()+"."+name, originClass))); //像documentContext的Models中添加我們新生成的Class
} catch (Exception e) {
e.printStackTrace();
}
parameterContext.parameterBuilder() //修改Map參數的ModelRef爲我們動態生成的class
.parameterType("body")
.modelRef(new ModelRef(name))
.name(name);
}
}
private Class createRefModelIgp(String[] propertys, String name, Class origin) throws NotFoundException {
ClassPool pool = ClassPool.getDefault();
CtClass ctClass = pool.makeClass( name);
try {
Field[] fields = origin.getDeclaredFields();
List<Field> fieldList = Arrays.asList(fields);
List<String> ignorePropertys = Arrays.asList(propertys);
List<Field> dealFileds = fieldList.stream().filter(s -> !ignorePropertys.contains(s.getName())).collect(Collectors.toList());
for (Field field : dealFileds) {
CtField ctField = new CtField(ClassPool.getDefault().get(field.getType().getName()), field.getName(), ctClass);
ctField.setModifiers(Modifier.PUBLIC);
ApiModelProperty ampAnno = origin.getDeclaredField(field.getName()).getAnnotation(ApiModelProperty.class);
String attributes = java.util.Optional.ofNullable(ampAnno).map(s->s.value()).orElse("");
if (StringUtils.isNotBlank(attributes) ){ //添加model屬性說明
ConstPool constPool = ctClass.getClassFile().getConstPool();
AnnotationsAttribute attr = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag);
Annotation ann = new Annotation(ApiModelProperty.class.getName(), constPool);
ann.addMemberValue("value", new StringMemberValue(attributes, constPool));
attr.addAnnotation(ann);
ctField.getFieldInfo().addAttribute(attr);
}
ctClass.addField(ctField);
}
return ctClass.toClass();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
@Override
public boolean supports(DocumentationType documentationType) {
return true;
}
}
步驟三:
public class AppCustomController {
@Autowired
private UserService userService;
@PostMapping("test1")
@ApiOperation("form測試1")
public R custom(@RequestBody RegisterForm form){
return R.ok();
}
@PostMapping("test2")
@ApiOperation("form測試2")
public R test2(@ApiIgp ({"name","addr"}) // 這裏把你不需要的DTO屬性中的值填寫上
@RequestBody RegisterForm form){
return R.ok();
}
}
總結
我這邊通過javassist這個類結合swagger 可拓展的插件去解決這個問題,通過自定義註解把不需要的屬性值在swagger頁面去掉,這裏只會影響頁面的顯示,不會影響到接收的參數。
拓展
當然這裏你也可以通過自定義MapApiReader的類去 "正着" 處理這個問題, 你可以 把需要的屬性留着,或者說 在每個請求統一加上某個屬性說明都是可以做到的。
ps(這裏javassist 最好使用alibaba的 異常不會影響整個swagger)