swagger2 同一个实体用在多个不同的controller接口展示不同的字段-get模式

这是之前写的支持body模式的 那个方案有个小问题 query(get)模式不支持 今儿让它支持一下

https://www.jianshu.com/p/09a4619fb0f7

1. 同样是自定义注解

黑名单模式


import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 自定义aop注解 支持swagger的动态属性 排除属性
 * @author xudaz
 */
@Target({ElementType.PARAMETER, ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiIgp {
    String[] value(); //对象属性值
}

白名单模式

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 自定义aop注解 支持swagger的动态属性 (只)需要属性
 * @author xudaz
 */
@Target({ElementType.PARAMETER, ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiNeed {
    //对象属性值
    String[] value();
}

实现swagger提供的插件 支持上面俩自定义注解


import com.fasterxml.classmate.ResolvedType;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.collect.FluentIterable;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestPart;
import springfox.documentation.builders.BuilderDefaults;
import springfox.documentation.builders.OperationBuilder;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.service.Operation;
import springfox.documentation.service.Parameter;
import springfox.documentation.service.ResolvedMethodParameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.schema.EnumTypeDeterminer;
import springfox.documentation.spi.service.OperationBuilderPlugin;
import springfox.documentation.spi.service.contexts.OperationContext;
import springfox.documentation.spi.service.contexts.ParameterContext;
import springfox.documentation.spring.web.plugins.DocumentationPluginsManager;
import springfox.documentation.spring.web.readers.operation.OperationParameterReader;
import springfox.documentation.spring.web.readers.parameter.ExpansionContext;
import springfox.documentation.spring.web.readers.parameter.ModelAttributeParameterExpander;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import static com.google.common.base.Predicates.not;
import static com.google.common.collect.Lists.newArrayList;
import static springfox.documentation.schema.Collections.isContainerType;
import static springfox.documentation.schema.Maps.isMapType;
import static springfox.documentation.schema.Types.isBaseType;
import static springfox.documentation.schema.Types.typeNameFor;

/**
 *  重写 swagger2 的 ModelAttributeParameterExpander 支持get请求的自定义注解
 * @author xudaz
 */
@SuppressWarnings({"Guava", "rawtypes"})
@Component("MyOperationBuilderPlugin")
@Order
@Slf4j
public class MyOperationBuilderPlugin extends OperationParameterReader implements OperationBuilderPlugin {

    private final EnumTypeDeterminer enumTypeDeterminer;
    private final ModelAttributeParameterExpander expander;

    private Boolean changed;

    @Autowired
    private DocumentationPluginsManager pluginsManager;

    @Autowired
    public MyOperationBuilderPlugin(ModelAttributeParameterExpander expander, EnumTypeDeterminer enumTypeDeterminer) {
        super(expander, enumTypeDeterminer);
        this.enumTypeDeterminer = enumTypeDeterminer;
        this.expander = expander;
    }


    /**
     * Implement this method to override the Operation using the OperationBuilder available in the context
     *
     * @param context - context that can be used to override the parameter attributes
     * @see Operation
     * @see OperationBuilder
     */
    @Override
    public void apply(OperationContext context) {
        changed = false;
        List<Parameter> parameters = readParameters(context);
        if (changed){
            // 反射给parameters赋值
            try {
                Field parametersField = OperationBuilder.class.getDeclaredField("parameters");
                parametersField.setAccessible(true);
                List<Parameter> source = BuilderDefaults.nullToEmptyList(parameters);
                parametersField.set(context.operationBuilder(), source);
            } catch ( Exception e ) {
                log.error("动态更改swagger参数错误", e);
            }
        }
        context.operationBuilder().parameters(context.getGlobalOperationParameters());
    }

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


    @SuppressWarnings("Guava")
    private List<Parameter> readParameters(final OperationContext context) {

        List<ResolvedMethodParameter> methodParameters = context.getParameters();
        List<Parameter> parameters = newArrayList();

        for (ResolvedMethodParameter methodParameter : methodParameters) {
            ResolvedType alternate = context.alternateFor(methodParameter.getParameterType());
            if (!shouldIgnore(methodParameter, alternate, context.getIgnorableParameterTypes())) {

                ParameterContext parameterContext = new ParameterContext(methodParameter,
                        new ParameterBuilder(),
                        context.getDocumentationContext(),
                        context.getGenericsNamingStrategy(),
                        context);

                List<Parameter> tempItems;

                if (shouldExpand(methodParameter, alternate)) {
                    tempItems = expander.expand(
                                    new ExpansionContext("", alternate, context));
                } else {
                    tempItems = new ArrayList<>(Collections.singleton(pluginsManager.parameter(parameterContext)));
//                    tempItems = new ArrayList<>();
                }

                // 判断遍历 是否有自定义注解 有就进行操作
                Optional<ApiIgp> apiIgpOptional = methodParameter.findAnnotation(ApiIgp.class);
                if ( apiIgpOptional.isPresent() ) {
                    String[] properties = apiIgpOptional.get().value();
                    tempItems = tempItems.stream().filter(parameter -> {

                        for (String property : properties) {
                            // 匹配黑名单
                            if ( property.equals(parameter.getName())
                                    || ! parameter.getName().contains(SwaggerConfig.MY_MODEL_NAME_PRE)
                                    || parameter.getName().startsWith(property + ".") ) {
                                return false;
                            }
                        }
                        return true;
                    }).collect(Collectors.toList());
                    changed = true;
                }

                Optional<ApiNeed> apiNeedOptional = methodParameter.findAnnotation(ApiNeed.class);
                if ( apiNeedOptional.isPresent() ) {
                    String[] properties = apiNeedOptional.get().value();
                    tempItems = tempItems.stream().filter(parameter -> {
                        for (String property : properties) {
                            // 匹配 白名单
                            if ( property.equals(parameter.getName())
                                    || parameter.getName().contains(SwaggerConfig.MY_MODEL_NAME_PRE)
                                    || parameter.getName().startsWith(property + ".") ) {
                                return true;
                            }
                        }
                        return false;
                    }).collect(Collectors.toList());
                    changed = true;
                }

                parameters.addAll(tempItems);
            }
        }

        return FluentIterable.from(parameters).filter(not(hiddenParams())).toList();
    }

    private Predicate<Parameter> hiddenParams() {
        return Parameter::isHidden;
    }

    private boolean shouldIgnore(
            final ResolvedMethodParameter parameter,
            ResolvedType resolvedParameterType,
            final Set<Class> ignorableParamTypes) {

        if (ignorableParamTypes.contains(resolvedParameterType.getErasedType())) {
            return true;
        }
        return FluentIterable.from(ignorableParamTypes)
                .filter(isAnnotation())
                .filter(parameterIsAnnotatedWithIt(parameter)).size() > 0;

    }

    private Predicate<Class> parameterIsAnnotatedWithIt(final ResolvedMethodParameter parameter) {
        return parameter::hasParameterAnnotation;
    }

    private Predicate<Class> isAnnotation() {
        return Annotation.class::isAssignableFrom;
    }

    private boolean shouldExpand(final ResolvedMethodParameter parameter, ResolvedType resolvedParamType) {
        return !parameter.hasParameterAnnotation(RequestBody.class)
                && !parameter.hasParameterAnnotation(RequestPart.class)
                && !parameter.hasParameterAnnotation(RequestParam.class)
                && !parameter.hasParameterAnnotation(PathVariable.class)
                && !isBaseType(typeNameFor(resolvedParamType.getErasedType()))
                && !enumTypeDeterminer.isEnum(resolvedParamType.getErasedType())
                && !isContainerType(resolvedParamType)
                && !isMapType(resolvedParamType);

    }
}

使用

image.png

效果

image.png

备注 springboot2 swagger2.9.2

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容