请注意,本文编写于 663 天前,最后修改于 640 天前,其中某些信息可能已经过时。
Swagger 枚举字段显示在接口文档中,并自定义枚举返回
最近使用枚举做为接收参数,接收枚举中的某一个属性,返回对应的枚举,但是发现这种方法不能在swagger文档中体现,故此拓展springfox插件来实现。
⚠️ 本次修改兼容 v2 v3
先来看下效果:
- 请求参数显示枚举信息
- 响应显示枚举信息
接下来实操
请求参数和响应显示枚举信息
创建枚举对应显示信息注解
其中
value
为入参数值,desc
为入参的描述@Target({ ElementType.TYPE }) @Retention(RetentionPolicy.RUNTIME) public @interface EnableSwaggerEnum { String value() default "value"; String desc() default "desc"; } @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface EnableSwaggerResponseEnum { String value() default "value"; String desc() default "desc"; }
创建一个公共修改model
由于要兼容v2
和v3
,故此需要创建一个修改的公共model,用来存放修改的枚举信息。其中,displayValues
是用来显示各个枚举值和信息的字符串,allowableListValues
为实际要显示的入场值。@Data public class EnumMidModel { private String displayValues; private AllowableListValues allowableListValues; }
封装model
public class SwaggerEnumUtils { /** * 封装枚举消息 * * @param type 此解析类型具有的类型擦除 Class<?> * @param enableSwaggerEnum 自定义swagger枚举限制注释 {@link EnableSwaggerEnum} * @return {@link EnumMidModel} * @author Mc * @since 2023/3/1 20:24 */ protected static EnumMidModel getEnumMidModel(Class<?> type, EnableSwaggerEnum enableSwaggerEnum) { EnumMidModel enumMidModel = new EnumMidModel(); String value = enableSwaggerEnum.value(); String desc = enableSwaggerEnum.desc(); Object[] enumConstants = type.getEnumConstants(); // 拼装显示内容 final String displayValues = Arrays.stream(enumConstants) .filter(Objects::nonNull) .map( item -> { Object valueVal = ReflectUtil.getFieldValue(item, value); Object descVal = ReflectUtil.getFieldValue(item, desc); return valueVal.toString() + " : " + descVal.toString(); } ) .collect(Collectors.joining("】\n【", "\n【", "】")); List<String> allowableValue = Arrays.stream(enumConstants) .filter(Objects::nonNull) .map(item -> ReflectUtil.getFieldValue(item, value).toString()) .collect(Collectors.toList()); AllowableListValues allowableListValues = new AllowableListValues(allowableValue, "string"); enumMidModel.setDisplayValues(displayValues); enumMidModel.setAllowableListValues(allowableListValues); return enumMidModel; } }
使用插件拓展
直接上代码,内容为分别继承ModelPropertyBuilderPlugin
、ParameterBuilderPlugin
、ModelPropertyBuilderPlugin
插件,改写3个插件的apply()
方法,supports()
方法代表兼容哪种,可以写死为true
。
静态代码块里的内容为响应添加制定义枚举。@Component @Slf4j @Primary public class EnumPropertyModelOperationBuilder implements ModelPropertyBuilderPlugin, ParameterBuilderPlugin, OperationBuilderPlugin { static Map<String, String> responseMap = new HashMap<>(); static { Class<EnableSwaggerResponseEnum> enableSwaggerResponseEnumClass = EnableSwaggerResponseEnum.class; Set<Class<?>> classes = ClassUtil.scanPackageByAnnotation("com.xinwei", enableSwaggerResponseEnumClass); if (CollUtil.isNotEmpty(classes)) { for (Class<?> aClass : classes) { if (aClass.isEnum()) { Object[] enumConstants = aClass.getEnumConstants(); Class<?>[] interfaces = aClass.getInterfaces(); boolean flag = false; for (Class<?> anInterface : interfaces) { if (anInterface.equals(IErrorCode.class)) { flag = true; break; } } if (!flag) { break; } EnableSwaggerResponseEnum annotation = AnnotationUtil.getAnnotation(aClass, EnableSwaggerResponseEnum.class); String desc = annotation.desc(); String value = annotation.value(); Arrays.stream(enumConstants).filter(Objects::nonNull).forEach(item -> { Object valueVal = ReflectUtil.getFieldValue(item, value); Object descVal = ReflectUtil.getFieldValue(item, desc); responseMap.put(valueVal.toString(), descVal.toString()); }); } } } } /** * {@link ApiModelProperty} 枚举处理 * 可选值必须为 {@link AllowableListValues},否则会带上旧的枚举name * {@link EnumerationElementFacetBuilder#allowedValues(Collection)} 不会清理旧的选项 * {@link EnumerationElementFacetBuilder#allowedValues(AllowableValues)} 会清理旧的选项 * * @param context {@link ParameterContext} * @author Mc * @since 2023/3/1 14:16 */ @Override @Order(Ordered.HIGHEST_PRECEDENCE + 999) public void apply(ModelPropertyContext context) { Optional<ApiModelProperty> annotation = Optional.empty(); if (context.getAnnotatedElement().isPresent()) { annotation = ApiModelProperties.findApiModePropertyAnnotation(context.getAnnotatedElement().get()); } if (context.getBeanPropertyDefinition().isPresent()) { annotation = Annotations.findPropertyAnnotation( context.getBeanPropertyDefinition().get(), ApiModelProperty.class); } final Class<?> rawPrimaryType = context.getBeanPropertyDefinition().get().getRawPrimaryType(); //过滤得到目标类型 if (annotation.isPresent() && Enum.class.isAssignableFrom(rawPrimaryType)) { EnableSwaggerEnum enableSwaggerEnum = AnnotationUtils.findAnnotation(rawPrimaryType, EnableSwaggerEnum.class); if (null != enableSwaggerEnum) { EnumMidModel enumMidModel = SwaggerEnumUtils.getEnumMidModel(rawPrimaryType, enableSwaggerEnum); context.getSpecificationBuilder() .description(annotation.get().value() + enumMidModel.getDisplayValues()) .enumerationFacet(e -> e.allowedValues(enumMidModel.getAllowableListValues())); } } } @Override public boolean supports(@NonNull DocumentationType documentationType) { return true; } /** * {@link Parameter} 枚举处理 * 可选值必须为 {@link AllowableListValues},否则会带上旧的枚举name * {@link EnumerationElementFacetBuilder#allowedValues(Collection)} 不会清理旧的选项 * {@link EnumerationElementFacetBuilder#allowedValues(AllowableValues)} 会清理旧的选项 * * @param context {@link ParameterContext} * @author Mc * @since 2023/3/1 14:16 */ @Override @Order(Ordered.HIGHEST_PRECEDENCE + 2999) public void apply(ParameterContext context) { Optional<Parameter> annotation = context.resolvedMethodParameter().findAnnotation(Parameter.class); if (annotation.isPresent()) { Class<?> type = context.resolvedMethodParameter().getParameterType().getErasedType(); if (Enum.class.isAssignableFrom(type)) { EnableSwaggerEnum enableSwaggerEnum = AnnotationUtils.findAnnotation(type, EnableSwaggerEnum.class); if (enableSwaggerEnum != null) { EnumMidModel enumMidModel = SwaggerEnumUtils.getEnumMidModel(type, enableSwaggerEnum); context.requestParameterBuilder() .description(annotation.get().description() + enumMidModel.getDisplayValues()) .query(a -> a.enumerationFacet(e -> e.allowedValues(enumMidModel.getAllowableListValues()))); } } } } /** * {@link io.swagger.v3.oas.annotations.Operation} 枚举处理 * 可选值必须为 {@link AllowableListValues},否则会带上旧的枚举name * * @param context {@link ParameterContext} * @author Mc * @since 2023/3/1 14:16 */ @Override @Order(Ordered.HIGHEST_PRECEDENCE + 999) public void apply(OperationContext context) { OperationBuilder operationBuilder = context.operationBuilder(); Operation build = operationBuilder.build(); // 添加响应 addResponseInfo(operationBuilder, build); List<ResolvedMethodParameter> parameters = context.getParameters(); parameters.forEach(parameter -> { ResolvedType parameterType = parameter.getParameterType(); Class<?> type = parameterType.getErasedType(); if (Enum.class.isAssignableFrom(type)) { EnableSwaggerEnum enableSwaggerEnum = AnnotationUtils.findAnnotation(type, EnableSwaggerEnum.class); if (enableSwaggerEnum != null) { EnumMidModel enumMidModel = SwaggerEnumUtils.getEnumMidModel(type, enableSwaggerEnum); Set<RequestParameter> requestParameters = build.getRequestParameters(); Set<RequestParameter> newParameters = new HashSet<>(); for (RequestParameter requestParameter : requestParameters) { if (requestParameter.getName().equals(parameter.defaultName().orElse(""))) { ParameterSpecification specification = requestParameter.getParameterSpecification(); Optional<SimpleParameterSpecification> query = specification.getQuery(); if (query.isPresent()) { SimpleParameterSpecification simpleParameterSpecification = query.get(); List<ElementFacet> newFacets = new ArrayList<>(); EnumerationFacet enumerationFacet = new EnumerationFacet(enumMidModel.getAllowableListValues().getValues()); newFacets.add(enumerationFacet); ReflectUtil.setFieldValue(simpleParameterSpecification, "facets", newFacets); } requestParameter = new RequestParameter(requestParameter.getName(), requestParameter.getIn(), enumMidModel.getDisplayValues() , requestParameter.getRequired(), requestParameter.getDeprecated(), requestParameter.getHidden(), specification, requestParameter.getScalarExample(), requestParameter.getExamples(), requestParameter.getPrecedence(), requestParameter.getExtensions(), requestParameter.getParameterIndex() ); } newParameters.add(requestParameter); } operationBuilder.requestParameters(newParameters); } } }); } private static void addResponseInfo(OperationBuilder operationBuilder, Operation build) { if (MapUtil.isNotEmpty(responseMap)) { List<Response> newResponseList = new ArrayList<>(); SortedSet<Response> responses = build.getResponses(); if (CollUtil.isNotEmpty(responses)) { newResponseList.addAll(responses); } for (Map.Entry<String, String> responseInfo : responseMap.entrySet()) { Example someDescription = new ExampleBuilder() .description(responseInfo.getValue()) .mediaType(MediaType.APPLICATION_JSON_VALUE) .id(responseInfo.getKey()) .value(R.failed(Long.valueOf(responseInfo.getKey()), responseInfo.getValue(), null)).build(); Response response = new ResponseBuilder() .code(responseInfo.getKey()) .description(responseInfo.getKey() + "_" + responseInfo.getValue()) .examples(Collections.singleton(someDescription)) .build(); newResponseList.add(response); } operationBuilder.responses(newResponseList); } } }
需要添加枚举显示的枚举类,添加注解
其中,value="code"
代表对应枚举属性code
,入场使用code
接收参数,desc="name"
代表对应描述为name
,文档会显示对应code
的name
描述需要添加响应显示的枚举类,添加注解
@EnableSwaggerResponseEnum
搞定看效果