Knife4j/Swagger显示自定义枚举类型注释

目录
  1. 需求
  2. 解决

需求

Knife4j/Swagger 自定义枚举类型的注释默认为枚举常量。拿AppType为例。

1
2
3
4
5
6
7
8
9
10
public enum AppType implements PersistEnum<Integer> {

ALI(1, "阿里云"),
YI_DONG(2, "移动云"),
LIAN_TONG(3, "联通云");

private Integer code;
private String desc;
...
}

默认会显示 可用值:ALI,YI_DONG,LIAN_TONG。
希望显示清楚自定义枚举的相关内容,如 1、阿里云,2、移动云,3、联通云。

解决

网络上搜索到的基本只能解决json请求及返回值的自定义,请求VO/POJO内的自定义枚举无法自定义显示,查看源码后进行编码,实现全部入参、返回值自定义枚举注释按需显示。对Swagger进行扩展,具体配置如下。

1、添加自定义注解类

1
2
3
4
5
6
7
8

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface SwaggerDisplayEnum {
String code() default "code";
String desc() default "desc";
}

2、自定义注解上加上SwaggerDisplayEnum注解,如下:

1
2
3
4
5
6
7
8
9
10
@SwaggerDisplayEnum
public enum AppType {

ALI(1, "阿里云"),
YI_DONG(2, "移动云"),
LIAN_TONG(3, "联通云");

private Integer code;
private String desc;
}

本项目code,desc使用注解默认值,具体按需配置注解参数 @SwaggerDisplayEnum(code=”xxx”, desc=”yyy”)。

3、添加Swagger扩展配置

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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
@Configuration
@Slf4j
public class SwaggerEnumPlugin implements ModelPropertyBuilderPlugin, ParameterBuilderPlugin, OperationBuilderPlugin, ExpandedParameterBuilderPlugin {

private static final Joiner joiner = Joiner.on(",");

@Override
public void apply(ModelPropertyContext context) {
Optional<BeanPropertyDefinition> optional = context.getBeanPropertyDefinition();
if (!optional.isPresent()) {
return;
}

Class<?> fieldType = optional.get().getField().getRawType();
addDescForEnum(context, fieldType);
}

private void addDescForEnum(ModelPropertyContext context, Class<?> fieldType) {
if (Enum.class.isAssignableFrom(fieldType)) {
SwaggerDisplayEnum anno = AnnotationUtils.findAnnotation(fieldType, SwaggerDisplayEnum.class);
if (anno != null) {
Object[] enumConstants = fieldType.getEnumConstants();
List<String> displayValues = getDisplayValues(anno, enumConstants);

ModelPropertyBuilder builder = context.getBuilder();
Field descField = ReflectionUtils.findField(builder.getClass(), "description");
ReflectionUtils.makeAccessible(descField);
String joinText = (ReflectionUtils.getField(descField, builder) == null ? "" : (ReflectionUtils.getField(descField, builder) + ":"))
+ joiner.join(displayValues);

builder.description(joinText).type(context.getResolver().resolve(Integer.class));
}
}
}

@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) {
Object[] enumConstants = clazz.getEnumConstants();
List<String> displayValues = getDisplayValues(annotation, enumConstants);
map.put(parameter.defaultName().orElse(""), 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;
}
}
});
}
}
});
}

@Override
public void apply(ParameterContext context) {
Class<?> type = context.resolvedMethodParameter().getParameterType().getErasedType();
ParameterBuilder parameterBuilder = context.parameterBuilder();
setAvailableValue(parameterBuilder, type);
}

private void setAvailableValue(ParameterBuilder parameterBuilder, Class<?> type) {
if (Enum.class.isAssignableFrom(type)) {
SwaggerDisplayEnum annotation = AnnotationUtils.findAnnotation(type, SwaggerDisplayEnum.class);
if (annotation != null) {
String code = annotation.code();
Object[] enumConstants = type.getEnumConstants();
List<String> displayValues = Arrays.stream(enumConstants).filter(Objects::nonNull).map(item -> {
Class<?> currentClass = item.getClass();

Field codeField = ReflectionUtils.findField(currentClass, code);
assert codeField != null;
ReflectionUtils.makeAccessible(codeField);
Object codeStr = ReflectionUtils.getField(codeField, item);
assert codeStr != null;
return codeStr.toString();

}).collect(Collectors.toList());

// 设置可用值
AllowableListValues values = new AllowableListValues(displayValues, "LIST");
parameterBuilder.allowableValues(values);
}
}
}

@Override
public void apply(ParameterExpansionContext context) {
Class<?> type = context.getFieldType().getErasedType();
ParameterBuilder parameterBuilder = context.getParameterBuilder();
if (Enum.class.isAssignableFrom(type)) {
setAvailableValue(parameterBuilder, type);
SwaggerDisplayEnum annotation = AnnotationUtils.findAnnotation(type, SwaggerDisplayEnum.class);
if (annotation != null) {
Object[] enumConstants = type.getEnumConstants();
List<String> displayValues = getDisplayValues(annotation, enumConstants);

Field descField = ReflectionUtils.findField(parameterBuilder.getClass(), "description");
ReflectionUtils.makeAccessible(descField);
String joinText = (ReflectionUtils.getField(descField, parameterBuilder) == null ? "" : (ReflectionUtils.getField(descField, parameterBuilder) + ":"))
+ joiner.join(displayValues);

parameterBuilder.description(joinText);
}
}
}

private List<String> getDisplayValues(SwaggerDisplayEnum annotation, Object[] enumConstants) {
if (annotation == null) {
return Lists.newArrayList();
}
String code = annotation.code();
String desc = annotation.desc();
return Arrays.stream(enumConstants).filter(Objects::nonNull).map(
item -> {
Class<?> currentClass = item.getClass();
Field codeField = ReflectionUtils.findField(currentClass, code);
assert codeField != null;
ReflectionUtils.makeAccessible(codeField);
Object codeStr = ReflectionUtils.getField(codeField, item);

Field descField = ReflectionUtils.findField(currentClass, desc);
assert descField != null;
ReflectionUtils.makeAccessible(descField);
Object descStr = ReflectionUtils.getField(descField, item);

return codeStr + "、" + descStr;
}
).collect(Collectors.toList());
}

@Override
public boolean supports(DocumentationType documentationType) {
return true;
}
}

其中,
ModelPropertyBuilderPlugin 适用于 application/json 提交的请求参数 及 json返回值 注释
ParameterBuilderPlugin、OperationBuilderPlugin 适用于请求方法上的直接枚举参数
ExpandedParameterBuilderPlugin 适用于POJO/VO内的枚举参数(网络上搜索到前几篇内容未实现的部分)

5、最终效果
Knife4j/Swagger自定义枚举1

Knife4j/Swagger自定义枚举2