zoukankan      html  css  js  c++  java
  • 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";
    }

     补个美美的效果图



  • 相关阅读:
    其他标签
    数组和全局变量
    字符串处理
    运算符
    PHP安装配置工具
    String、StringBuffer与StringBuilder之间区别
    mybits——1
    异常
    ubuntu 系统错误:Error : BrokenCount > 0解决
    ubuntu配置VScode
  • 原文地址:https://www.cnblogs.com/Mr-lin66/p/13366841.html
Copyright © 2011-2022 走看看