承接上篇文章 《一站式解決使用枚舉的各種痛點》 文章最后提到:在使用 swagger 來編寫接口文檔時艾猜,需要告訴前端枚舉類型有哪些取值,每次增加取值之后捻悯,不僅要改代碼箩朴,還要找到對應的取值在哪里使用了,然后修改 swagger 文檔秋度。反正小黑我覺得這樣做很不爽炸庞,那有沒有什么辦法可以讓 swagger 框架來幫我們自動列舉出所有的枚舉數(shù)值呢?
這期小黑同學就來講講解決方案荚斯。
先來看一下效果埠居,有一個感性的認識
swagger
請注意哦,這里是課程類型不是我們手動列舉出來的事期,是swagger框架幫我們自動列舉的滥壕。對應的代碼如下:
代碼
那么,這是怎么做到的呢兽泣?
簡單描述一下實現(xiàn):
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.put(courseType.type, courseType);
}
mappings = Collections.unmodifiableMap(temp);
}
@EnumConvertMethod
@JsonCreator(mode = JsonCreator.Mode.DELEGATING)
@Nullable
public static CourseType resolve(int index) {
return mappings.get(index);
}
}
3狂票、實現(xiàn) ModelPropertyBuilderPlugin
接口,擴展 swagger熙暴,實現(xiàn)在文檔中列舉所有的枚舉值闺属。
public class EnumModelPropertyBuilderPlugin implements ModelPropertyBuilderPlugin {
@Override
public void apply(ModelPropertyContext context) {
Optional<BeanPropertyDefinition> optional = context.getBeanPropertyDefinition();
if (!optional.isPresent()) {
return;
}
final Class<?> fieldType = optional.get().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 = fieldType.getEnumConstants();
List<String> displayValues =
Arrays.stream(enumConstants)
.filter(Objects::nonNull)
.map(item -> {
Class<?> currentClass = item.getClass();
Field indexField = ReflectionUtils.findField(currentClass, index);
ReflectionUtils.makeAccessible(indexField);
Object value = ReflectionUtils.getField(indexField, item);
Field descField = ReflectionUtils.findField(currentClass, name);
ReflectionUtils.makeAccessible(descField);
Object desc = ReflectionUtils.getField(descField, item);
return value + ":" + desc;
}).collect(Collectors.toList());
ModelPropertyBuilder builder = context.getBuilder();
Field descField = ReflectionUtils.findField(builder.getClass(), "description");
ReflectionUtils.makeAccessible(descField);
String joinText = ReflectionUtils.getField(descField, builder)
+ " (" + String.join("; ", displayValues) + ")";
builder.description(joinText).type(context.getResolver().resolve(Integer.class));
}
}
}
}
4、實現(xiàn) ParameterBuilderPlugin
和 OperationBuilderPlugin
接口周霉,列舉枚舉參數(shù)的所有取值掂器。
public class EnumParameterBuilderPlugin implements ParameterBuilderPlugin, OperationBuilderPlugin {
private static final Joiner joiner = Joiner.on(",");
@Override
public void apply(ParameterContext context) {
Class<?> type = context.resolvedMethodParameter().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 = type.getEnumConstants();
List<String> displayValues = Arrays.stream(enumConstants).filter(Objects::nonNull).map(item -> {
Class<?> currentClass = item.getClass();
Field indexField = ReflectionUtils.findField(currentClass, index);
ReflectionUtils.makeAccessible(indexField);
Object value = ReflectionUtils.getField(indexField, item);
Field descField = ReflectionUtils.findField(currentClass, name);
ReflectionUtils.makeAccessible(descField);
Object desc = ReflectionUtils.getField(descField, item);
return value.toString();
}).collect(Collectors.toList());
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 = context.getParameters();
parameters.forEach(parameter -> {
ResolvedType parameterType = parameter.getParameterType();
Class<?> clazz = parameterType.getErasedType();
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 = clazz.getEnumConstants();
List<String> displayValues = Arrays.stream(enumConstants).filter(Objects::nonNull).map(item -> {
Class<?> currentClass = item.getClass();
Field indexField = ReflectionUtils.findField(currentClass, index);
ReflectionUtils.makeAccessible(indexField);
Object value = ReflectionUtils.getField(indexField, item);
Field descField = ReflectionUtils.findField(currentClass, name);
ReflectionUtils.makeAccessible(descField);
Object desc = ReflectionUtils.getField(descField, item);
return value + ":" + desc;
}).collect(Collectors.toList());
map.put(parameter.defaultName().or(""), displayValues);
OperationBuilder operationBuilder = context.operationBuilder();
Field parametersField = ReflectionUtils.findField(operationBuilder.getClass(), "parameters");
ReflectionUtils.makeAccessible(parametersField);
List<Parameter> list = (List<Parameter>) ReflectionUtils.getField(parametersField, operationBuilder);
map.forEach((k, v) -> {
for (Parameter currentParameter : list) {
if (StringUtils.equals(currentParameter.getName(), k)) {
Field description = ReflectionUtils.findField(currentParameter.getClass(), "description");
ReflectionUtils.makeAccessible(description);
Object field = ReflectionUtils.getField(description, currentParameter);
ReflectionUtils.setField(description, currentParameter, field + " , " + joiner.join(v));
break;
}
}
});
}
}
});
}
}
這篇文章比較枯燥,小黑我也不知道該怎么去講述俱箱,只是將源碼附錄了出來唉匾。如果有讀者看了之后還是不清楚的話,可以給我留言匠楚,我會一一解答巍膘。感謝你的閱讀~~
相關源碼已經(jīng)上傳到了 github:https://github.com/shenjianeng/solution-for-enums