真⾹警告!扩展swagger⽀持⽂档⾃动列举所有枚举值
承接上篇⽂章⽂章最后提到:在使⽤ swagger 来编写接⼝⽂档时,需要告诉前端枚举类型有哪些取值,每次增加取值之后,不仅要改代码,还要到对应的取值在哪⾥使⽤了,然后修改 swagger ⽂档。反正⼩⿊我觉得这样做很不爽,那有没有什么办法可以让 swagger 框架来帮我们⾃动列举出所有的枚举数值呢?
这期⼩⿊同学就来讲讲解决⽅案。
先来看⼀下效果,有⼀个感性的认识
请注意哦,这⾥是课程类型不是我们⼿动列举出来的,是swagger框架帮我们⾃动列举的。对应的代码如下:
那么,这是怎么做到的呢?
简单描述⼀下实现:
1、⾃定义 SwaggerDisplayEnum 注解,注解中有两个属性,这两个属性是⽤来⼲什么的呢?⼩⿊我先不说,⼤家往下阅读,相信就能明⽩啦~
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface SwaggerDisplayEnum {
String index() default "index";
String name() default "name";
}
2、在我们的⾃定义枚举类中标记@SwaggerDisplayEnum注解
@Getter
@AllArgsConstructor
@SwaggerDisplayEnum(index = "type", name = "desc")
public enum CourseType {
/**
* 图⽂
*/
PICTURE(102, "图⽂"),
/**
* ⾳频
*/
AUDIO(103, "⾳频"),
/**
* 视频
*/
VIDEO(104, "视频"),
/
**
* 外链
*/
URL(105, "外链"),
;
@JsonValue
private final int type;
private final String desc;
private static final Map<Integer, CourseType> mappings;
static {
Map<Integer, CourseType> temp = new HashMap<>();
for (CourseType courseType : values()) {
temp.pe, courseType);
}
mappings = Collections.unmodifiableMap(temp);
}
@EnumConvertMethod
@JsonCreator(mode = JsonCreator.Mode.DELEGATING)
@Nullable
public static CourseType resolve(int index) {
(index);
}
}
3、实现ModelPropertyBuilderPlugin接⼝,扩展 swagger,实现在⽂档中列举所有的枚举值。
public class EnumModelPropertyBuilderPlugin implements ModelPropertyBuilderPlugin {
@Override
public void apply(ModelPropertyContext context) {
Optional<BeanPropertyDefinition> optional = BeanPropertyDefinition();
if (!optional.isPresent()) {
return;
}
final Class<?> fieldType = ().getField().getRawType();
addDescForEnum(context, fieldType);
}
@Override
public boolean supports(DocumentationType delimiter) {
接口文档怎么看return true;
}
private void addDescForEnum(ModelPropertyContext context, Class<?> fieldType) {
if (Enum.class.isAssignableFrom(fieldType)) {
SwaggerDisplayEnum annotation = AnnotationUtils.findAnnotation(fieldType, SwaggerDisplayEnum.class);            if (annotation != null) {
String index = annotation.index();
String name = annotation.name();
Object[] enumConstants = EnumConstants();
List<String> displayValues =
Arrays.stream(enumConstants)
.filter(Objects::nonNull)
.map(item -> {
Class<?> currentClass = Class();
Field indexField = ReflectionUtils.findField(currentClass, index);
ReflectionUtils.makeAccessible(indexField);
Object value = Field(indexField, item);
Field descField = ReflectionUtils.findField(currentClass, name);
ReflectionUtils.makeAccessible(descField);
Object desc = Field(descField, item);
return value + ":" + desc;
}).List());
ModelPropertyBuilder builder = Builder();
Field descField = ReflectionUtils.Class(), "description");
ReflectionUtils.makeAccessible(descField);
String joinText = Field(descField, builder)
+ " (" + String.join("; ", displayValues) + ")";
builder.description(joinText).Resolver().resolve(Integer.class));
}
}
}
}
4、实现ParameterBuilderPlugin和OperationBuilderPlugin接⼝,列举枚举参数的所有取值。
public class EnumParameterBuilderPlugin implements ParameterBuilderPlugin, OperationBuilderPlugin {
private static final Joiner joiner = (",");
@Override
public void apply(ParameterContext context) {
Class<?> type = solvedMethodParameter().getParameterType().getErasedType();
if (Enum.class.isAssignableFrom(type)) {
SwaggerDisplayEnum annotation = AnnotationUtils.findAnnotation(type, SwaggerDisplayEnum.class);
if (annotation != null) {
String index = annotation.index();
String name = annotation.name();
Object[] enumConstants = EnumConstants();
List<String> displayValues = Arrays.stream(enumConstants).filter(Objects::nonNull).map(item -> {
Class<?> currentClass = Class();
Field indexField = ReflectionUtils.findField(currentClass, index);
ReflectionUtils.makeAccessible(indexField);
Object value = Field(indexField, item);
Field descField = ReflectionUtils.findField(currentClass, name);
ReflectionUtils.makeAccessible(descField);
Object desc = Field(descField, item);
String();
}).List());
ParameterBuilder parameterBuilder = context.parameterBuilder();
AllowableListValues values = new AllowableListValues(displayValues, "LIST");
parameterBuilder.allowableValues(values);
}
}
}
@Override
public boolean supports(DocumentationType delimiter) {
return true;
}
@Override
public void apply(OperationContext context) {
Map<String, List<String>> map = new HashMap<>();
List<ResolvedMethodParameter> parameters = Parameters();
parameters.forEach(parameter -> {
ResolvedType parameterType = ParameterType();
Class<?> clazz = ErasedType();
if (Enum.class.isAssignableFrom(clazz)) {
SwaggerDisplayEnum annotation = AnnotationUtils.findAnnotation(clazz, SwaggerDisplayEnum.class);
if (annotation != null) {
String index = annotation.index();
String name = annotation.name();
Object[] enumConstants = EnumConstants();
List<String> displayValues = Arrays.stream(enumConstants).filter(Objects::nonNull).map(item -> {
Class<?> currentClass = Class();
Field indexField = ReflectionUtils.findField(currentClass, index);
ReflectionUtils.makeAccessible(indexField);
Object value = Field(indexField, item);
Field descField = ReflectionUtils.findField(currentClass, name);
ReflectionUtils.makeAccessible(descField);
Object desc = Field(descField, item);
return value + ":" + desc;
}).List());
map.put(parameter.defaultName().or(""), displayValues);
OperationBuilder operationBuilder = context.operationBuilder();
Field parametersField = ReflectionUtils.Class(), "parameters");
ReflectionUtils.makeAccessible(parametersField);
List<Parameter> list = (List<Parameter>) Field(parametersField, operationBuilder);
map.forEach((k, v) -> {
for (Parameter currentParameter : list) {
if (StringUtils.Name(), k)) {
Field description = ReflectionUtils.Class(), "description");
ReflectionUtils.makeAccessible(description);
Object field = Field(description, currentParameter);
ReflectionUtils.setField(description, currentParameter, field + " , " + joiner.join(v));
break;
}
}
});
}
}
});
}
}
这篇⽂章⽐较枯燥,⼩⿊我也不知道该怎么去讲述,只是将源码附录了出来。如果有读者看了之后还是不清楚的话,可以给我留⾔,我会⼀⼀解答。感谢你的阅读~~