Swagger 枚举字段显示在接口文档中,并自定义枚举返回

最近使用枚举做为接收参数,接收枚举中的某一个属性,返回对应的枚举,但是发现这种方法不能在swagger文档中体现,故此拓展springfox插件来实现。

⚠️ 本次修改兼容 v2 v3

先来看下效果:

  1. 请求参数显示枚举信息
  2. 响应显示枚举信息

接下来实操

请求参数和响应显示枚举信息

  1. 创建枚举对应显示信息注解

    其中 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";
    }
  2. 创建一个公共修改model
    由于要兼容v2v3,故此需要创建一个修改的公共model,用来存放修改的枚举信息。其中,displayValues是用来显示各个枚举值和信息的字符串,allowableListValues 为实际要显示的入场值。

    @Data
    public class EnumMidModel {
    
        private String displayValues;
    
        private AllowableListValues allowableListValues;
    
    }
  3. 封装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;
        }
    }
  4. 使用插件拓展
    直接上代码,内容为分别继承ModelPropertyBuilderPluginParameterBuilderPluginModelPropertyBuilderPlugin插件,改写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);
            }
        }
    }
  5. 需要添加枚举显示的枚举类,添加注解
    其中,value="code" 代表对应枚举属性code,入场使用code 接收参数,desc="name" 代表对应描述为name,文档会显示对应codename描述

  6. 需要添加响应显示的枚举类,添加注解@EnableSwaggerResponseEnum

  7. 搞定看效果

如果觉得我的文章对你有用,请随意赞赏