spring boot swagger2 接口多版本控制踩坑记录

引入包:

        <!--swagger-->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.9.2</version>
            <exclusions>
                <exclusion>
                    <groupId>io.swagger</groupId>
                    <artifactId>swagger-annotations</artifactId>
                </exclusion>

                <exclusion>
                    <groupId>io.swagger</groupId>
                    <artifactId>swagger-models</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <!--解决进入swagger页面报类型转换错误,排除2.9.2中的引用,手动增加1.5.21版本-->
        <dependency>
            <groupId>io.swagger</groupId>
            <artifactId>swagger-annotations</artifactId>
            <version>1.5.21</version>
        </dependency>

        <dependency>
            <groupId>io.swagger</groupId>
            <artifactId>swagger-models</artifactId>
            <version>1.5.21</version>
        </dependency>
        <!-- swagger2 增强UI ,拥有好看的界面, 和接口分组,排序等功能,如不引用可自行删除-->
        <!-- https://mvnrepository.com/artifact/com.github.xiaoymin/knife4j-spring-boot-starter -->
        <!-- https://doc.xiaominfo.com/-->
        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>knife4j-spring-boot-starter</artifactId>
            <version>2.0.4</version>
        </dependency>

 

swagger配置并启用:
import com.culturalCenter.placeManage.globalConfig.Interface.ApiVersion;
import com.culturalCenter.placeManage.globalConfig.Interface.CustomVersion;
import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Profile;
import org.springframework.web.bind.annotation.RequestMethod;
import springfox.bean.validators.configuration.BeanValidatorPluginsConfiguration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.builders.ResponseMessageBuilder;
import springfox.documentation.service.*;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;

/**
 * 解释接口信息的一些注解
 *
 * @author wu
 * @Api:修饰整个类,描述Controller的作用
 * @ApiOperation:描述一个类的一个方法,或者说一个接口
 * @ApiParam:单个参数描述
 * @ApiModel:用对象来接收参数
 * @ApiProperty:用对象接收参数时,描述对象的一个字段
 * @ApiResponse:HTTP响应其中1个描述
 * @ApiResponses:HTTP响应整体描述
 * @ApiIgnore:使用该注解忽略这个API
 * @ApiError :发生错误返回的信息
 * @ApiParamImplicitL:一个请求参数
 * @ApiParamsImplicit 多个请求参数
 * @Profile 注解 标识加载在dev和test文件使用
 */
@Configuration
@EnableSwagger2
@EnableKnife4j
@Import(BeanValidatorPluginsConfiguration.class)
@Profile("dev")
public class SwaggerConfig implements InitializingBean {

    private Logger log = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private ApplicationContext applicationContext;

    /**
     * 获取swagger创建初始化信息
     *
     * @return
     */
    @SuppressWarnings("deprecation")
    public  ApiInfo apiInfo() {
        return new ApiInfoBuilder().title("接口文档").
                description("服务端通用接口").version("1.0").build();
    }

    /**
     * 安全认证参数
     *
     * @return
     */
    private List<ApiKey> securitySchemes() {
        List<ApiKey> apiKeyList = new ArrayList<ApiKey>();
        apiKeyList.add(new ApiKey("Authorization", "Authorization", "header"));
        return apiKeyList;
    }

    private List<SecurityContext> securityContexts() {
        List<SecurityContext> securityContexts = new ArrayList<>();
        securityContexts.add(
                SecurityContext.builder()
                        .securityReferences(defaultAuth())
                        .forPaths(PathSelectors.regex("^(?!auth).*$"))
                        .build());
        return securityContexts;
    }

    private List<SecurityReference> defaultAuth() {
        AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
        AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
        authorizationScopes[0] = authorizationScope;
        List<SecurityReference> securityReferences = new ArrayList<>();
        securityReferences.add(new SecurityReference("Authorization", authorizationScopes));
        return securityReferences;
    }


    /**
     * 创建全局响应值
     *
     * @return
     */
    private List<ResponseMessage> responseBuilder() {
        List<ResponseMessage> responseMessageList = new ArrayList<>();
//        List<Map<String, Object>> resultCode = getAllEnum("com.culturalCenter.placeManage.Utils.ResultCode");
//        for (Map<String,Object> item :resultCode) {
//            responseMessageList.add(new ResponseMessageBuilder().code(Integer.valueOf(item.get("code").toString())).message(item.get("message").toString()).build());
//        }
        responseMessageList.add(new ResponseMessageBuilder().code(200).message("响应成功").build());
        responseMessageList.add(new ResponseMessageBuilder().code(500).message("服务器内部错误").build());
        return responseMessageList;
    }

    /**
     * 根据枚举的字符串获取枚举的值
     *
     * @param className 包名+类名
     * @return
     * @throws Exception
     */
    public List<Map<String, Object>> getAllEnum(String className) {
        try {
            // 得到枚举类对象
            Class<Enum> clazz = (Class<Enum>) Class.forName(className);
            List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
            //获取所有枚举实例
            Enum[] enumConstants = clazz.getEnumConstants();
            //根据方法名获取方法
            Method getCode = clazz.getMethod("getCode");
            Method getMessage = clazz.getMethod("getMessage");
            Map<String, Object> map = null;
            for (Enum enum1 : enumConstants) {
                map = new HashMap<String, Object>();
                //执行枚举方法获得枚举实例对应的值
                map.put("code", getCode.invoke(enum1));
                map.put("message", getMessage.invoke(enum1));
                list.add(map);
            }
            return list;
        } catch (Exception e) {
            log.error(e.getMessage());
            return null;
        }
    }


   private Docket buildDocket(String groupName) {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .groupName(groupName)
                .select()
                .apis(method -> {
                    // 每个方法会进入这里进行判断并归类到不同分组,**请不要调换下面两段代码的顺序,在方法上的注解有优先级**
                    // 该方法上标注了版本
                    if (method.isAnnotatedWith(ApiVersion.class)) {
                        ApiVersion apiVersion = method.getHandlerMethod().getMethodAnnotation(ApiVersion.class);
                        if (apiVersion.value() != null) {
                            if (Arrays.asList(apiVersion.value()).contains(groupName)) {
                                return true;
                            }
                        }

                    }

                    // 方法所在的类是否标注了?
                    ApiVersion annotationOnClass = method.getHandlerMethod().getBeanType().getAnnotation(ApiVersion.class);
                    if (annotationOnClass != null) {
                        if (annotationOnClass.value() != null) {
                            if (Arrays.asList(annotationOnClass.value()).contains(groupName)) {
                                return true;
                            }
                        }
                    }
//此处可以增加一下没有版本控制的显示
          if (method.groupName().equals("token-controller")) return true;
            return false;
                })
                .paths(PathSelectors.any())
                .build().securitySchemes(securitySchemes()).securityContexts(securityContexts());
    }

    /**
     * 动态得创建Docket bean
     *
     * @throws Exception
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        // ApiConstantVersion 里面定义的每个变量会成为一个docket
        Class<CustomVersion> clazz = CustomVersion.class;
        Field[] declaredFields = clazz.getDeclaredFields();

        // 动态注入bean
        AutowireCapableBeanFactory autowireCapableBeanFactory = applicationContext.getAutowireCapableBeanFactory();
        if (autowireCapableBeanFactory instanceof DefaultListableBeanFactory) {

            DefaultListableBeanFactory capableBeanFactory = (DefaultListableBeanFactory) autowireCapableBeanFactory;
            for (Field declaredField : declaredFields) {

                // 要注意 "工厂名和方法名",意思是用这个bean的指定方法创建docket
                AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder
                        .genericBeanDefinition()
//此处的swaggerConfig首字母要小写否则报找不到bean的方法 .setFactoryMethodOnBean(
"buildDocket", "swaggerConfig") .addConstructorArgValue(declaredField.get(CustomVersion.class)).getBeanDefinition(); capableBeanFactory.registerBeanDefinition(declaredField.getName(), beanDefinition); } } } }

自定义注解实API版本控制:

ApiVersion.class
/**
 * API版本控制注解
 * Created on 2019/4/18 11:17.
 *
 * @author caogu
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface ApiVersion {
//    //标识版本号
//    int value() default 1;

    /**
     * 分组名(即版本号),可以传入多个
     *
     * @return
     */
    String value() default "v1";
}

 

自定义一个API版本号控制类

/**
 * 版本管理接口
 */
public interface CustomVersion {
    String VERSION_1 = "v1";
    String VERSION_2 = "v2";
}

 补个美美的效果图

 

 



 

posted @ 2020-07-23 16:02  曾经是最好  阅读(2202)  评论(0编辑  收藏  举报