Spring Boot 低版本、高版本集成swagger、springdoc-openapi-ui 配置以及注意事项!

2,206 阅读5分钟

简介

本文将详细的介绍 SpringBoot 低版本(2.2.6.Release)、高版本(2.7.18) 集成 swagger配置,还有最后会介绍springdoc-openapi-ui的集成,避免日后使用多走弯路,这里建议集成springdoc-openapi-ui,这个目前在维护,springfox-swagger目前已经不维护了,springfox-swagger对于Spring Boot高版本兼容的也不是很好,网络上的解决方案也是出奇的一致!!

SpringBoot 2.2.6.Release 集成 swagger

添加依赖

<!-- swagger start -->
<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>
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger-ui</artifactId>
    <version>2.9.2</version>
</dependency>
<!--解决Swagger 2.9.2版本NumberFormatException-->
<dependency>
    <groupId>io.swagger</groupId>
    <artifactId>swagger-models</artifactId>
    <version>1.6.0</version>
</dependency>
<dependency>
    <groupId>io.swagger</groupId>
    <artifactId>swagger-annotations</artifactId>
    <version>1.6.0</version>
</dependency>
<!-- swagger end -->

配置swagger静态资源访问路径

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled=true)
public class WebConfig extends WebMvcConfigurationSupport {


    @Autowired
    private AccessInterceptor interceptor;


    /**
     * 注册自定义拦截器
     * @param registry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(interceptor)
                .excludePathPatterns("/swagger-ui.html", "/webjars/**", "/v2/**","/swagger-resources/**", "/error/**"); // 不拦截 swagger 访问路径
    }


    /**
     * 发现如果继承了WebMvcConfigurationSupport, 需要重新指定静态资源
     * 注册自定义静态资源
     * @param registry
     */
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        // 解决静态资源无法访问
        registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");
        // 解决swagger无法访问
        registry.addResourceHandler("/swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
        // 解决swagger的js文件无法访问
        registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
        super.addResourceHandlers(registry);
    }
}

注意:

WebMvcConfigurationSupport 这个2.2.6.Release 版本中,如果是更老的版本可能是 WebMvcConfigurerAdapter

配置swagger配置类

/** 
  * <p>  </p> 
  * 
  * @site https://www.motopa.cn 
  * @author daizhao 
  * @date 2022/12/12 12:12
  */
@Configuration
@EnableSwagger2 //开启swagger2
public class SwaggerConfig  {


    /**
     * 构建swagger 配置简介、认证插件、扫描包的路径
     * @return
     */
    @Bean
    public Docket webApiConfig(){
        Docket docket = new Docket(DocumentationType.SWAGGER_2)
                .groupName("webApi")
                .apiInfo(webApiInfo())
                .select()
                .apis(RequestHandlerSelectors.withClassAnnotation(Api.class)) //通过注解Api
                //.apis(RequestHandlerSelectors.basePackage("com.miliqk.manage.platform.controller")) //通过指定包的路径
                .paths(PathSelectors.any())
                .build();


        //配置认证插件
        docket.securitySchemes(securitySchemes()).securityContexts(securityContexts());
        return docket;
    }


    private ApiInfo webApiInfo(){
        return new ApiInfoBuilder()
                .title("API文档")
                .description("API文档接口定义")
                .version("1.0")
                .contact(new Contact("java", "http://localhost:9330/login", "xxxxx@qq.com"))
                .build();
    }


    /**
     * 设置请求头信息
     * @return
     */
    private List<SecurityScheme> securitySchemes() {
        List<SecurityScheme> result = new ArrayList<>();
        result.add(new ApiKey("Authorization", "Authorization", "header"));
        result.add(new ApiKey("unique", "unique", "header"));
        return result;
    }


    /**
     * 设置需要登录认证的路径
     * @return
     */
    private List<SecurityContext> securityContexts() {
        List<SecurityContext> result = new ArrayList<>();
        result.add(getContextByPath("/*/.*"));
        return result;
    }


    private SecurityContext getContextByPath(String pathRegex) {
        return SecurityContext.builder()
                .securityReferences(defaultAuth())
                .forPaths(PathSelectors.regex(pathRegex))
                .build();
    }


    private List<SecurityReference> defaultAuth() {
        List<SecurityReference> result = new ArrayList<>();
        result.add(new SecurityReference("Authorization", scopes()));
        result.add(new SecurityReference("unique", scopes()));
        return result;
    }


    private AuthorizationScope[] scopes() {
        return new AuthorizationScope[]{new AuthorizationScope("global", "accessAnything")};
    }


}

常用注解

注解描述
@Api用于描述整个API接口的信息,包括API的标题、描述等。
@ApiOperation用于描述单个接口的操作信息,包括接口的HTTP方法、路径、描述等。
@ApiParam用于描述接口参数的信息,包括参数名、类型、描述等。
@ApiModel用于描述接口返回结果或请求体的模型信息。
@ApiModelProperty用于描述模型属性的信息,包括属性名、类型、描述等。
@ApiIgnore用于忽略某个API接口,使其不在生成的文档中显示。
@ApiResponse用于描述接口的响应信息,包括响应码、描述、返回类型等。
@ApiResponses用于描述多个接口响应的信息,可以与@ApiResponse配合使用。

配置完后,默认的访问地址:http://localhost:port/swagger-ui.html

注意: 

如果有权限路径的拦截配置,需要放行该地址 

目前springfox对于Spring Boot的版本支持比较落后,比如3.0.0版本是他们唯一的release版本,能支持Spring Boot 2.2及以上版本。对于2.2以下的版本,不能使用springfox-boot-starter,而是需要手动引入springfox-swagger2以及springfox-swagger-ui。但是springfox-boot-starter 3.0.0版本对于高版本的Spring Boot也并不能很好的支持,fox-boot-starter兼容。比如Spring Boot 2.7.x就不能和spring

SpringBoot 2.7.18 集成 swagger

配置依赖

<!-- swagger start -->


            <!-- Swagger3 -->
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-boot-starter</artifactId>
                <version>3.0.0</version>
            </dependency>


            <!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui -->
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger-ui</artifactId>
                <version>3.0.0</version>
            </dependency>


            <!-- https://mvnrepository.com/artifact/com.github.xiaoymin/knife4j-spring-boot-starter -->
            <dependency>
                <groupId>com.github.xiaoymin</groupId>
                 <artifactId>knife4j-spring-boot-starter</artifactId>
                <version>3.0.3</version>
            </dependency>
<!-- swagger end -->

配置swagger静态资源访问路径

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebConfig extends WebMvcConfigurationSupport {


    @Autowired
    private AccessInterceptor interceptor;


    /**
     * 注册自定义拦截器
     *
     * @param registry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(interceptor)
                .excludePathPatterns("/swagger-ui/**","/swagger-ui.html", "/webjars/**", "/v2/**", "/swagger-resources/**", "/error/**"); // 不拦截 swagger 访问路径
    }


    /**
     * 发现如果继承了WebMvcConfigurationSupport, 需要重新指定静态资源
     * 注册自定义静态资源
     *
     * @param registry
     */
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/**")
                .addResourceLocations("classpath:/static/");
        registry.addResourceHandler("doc.html")
                .addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**")
                .addResourceLocations("classpath:/META-INF/resources/webjars/");
        registry.addResourceHandler("/swagger-ui/**")
                .addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/");


    }
}

注意 :

这里的swagger-ui的路径 与 之前低版本可不同需要注意,观察源码可以发现,以前的是swagger-ui.html、现在改成了 index.html

配置swagger配置类

@Configuration
@EnableOpenApi
public class SwaggerConfig {
    /**
     * 构建swagger 配置简介、认证插件、扫描包的路径
     *
     * @return
     */
    @Bean
    public Docket webApiConfig() {
        Docket docket = new Docket(DocumentationType.SWAGGER_2)
                .groupName("webApi")
                .apiInfo(webApiInfo())
                .enable(true)
                .select()
                .apis(RequestHandlerSelectors.withClassAnnotation(Api.class)) //通过注解Api
                //.apis(RequestHandlerSelectors.basePackage("com.yichuang.platform.controller")) //通过指定包的路径
                .paths(PathSelectors.any())
                .build();


        //配置认证插件
        docket.securitySchemes(securitySchemes()).securityContexts(securityContexts());
        return docket;
    }


    private ApiInfo webApiInfo() {
        return new ApiInfoBuilder()
                .title("咪哩快看后台API文档")
                .description("本文档描述了咪哩快看后台API文档接口定义")
                .version("1.0")
                .contact(new Contact("java", "http://localhost:9660/login", "xxxxx@qq.com"))
                .build();
    }


    /**
     * 设置请求头信息
     *
     * @return
     */
    private List<SecurityScheme> securitySchemes() {
        List<SecurityScheme> result = new ArrayList<>();
        result.add(new ApiKey("Authorization", "Authorization", "header"));
        result.add(new ApiKey("unique", "unique", "header"));
        return result;
    }


    /**
     * 设置需要登录认证的路径
     *
     * @return
     */
    private List<SecurityContext> securityContexts() {
        List<SecurityContext> result = new ArrayList<>();
        result.add(getContextByPath("/*/.*"));
        return result;
    }


    private SecurityContext getContextByPath(String pathRegex) {
        return SecurityContext.builder()
                .securityReferences(defaultAuth())
                .forPaths(PathSelectors.regex(pathRegex))
                .build();
    }


    private List<SecurityReference> defaultAuth() {
        List<SecurityReference> result = new ArrayList<>();
        result.add(new SecurityReference("Authorization", scopes()));
        result.add(new SecurityReference("unique", scopes()));
        return result;
    }


    private AuthorizationScope[] scopes() {
        return new AuthorizationScope[]{new AuthorizationScope("global", "accessAnything")};
    }
}

设置Springfox 路径匹配规则

设置Springfox作用,解决如下错误 :

org.springframework.context.ApplicationContextException: Failed to start bean 'documentationPluginsBootstrapper'; nested exception

Springfox使用的路径匹配是基于AntPathMatcher,而Spring Boot 2.6.X 以后的版本使用的是PathPatternMatcher,所以在匹配路径时,找不到,所以修改排除为null的映射路径即可。

解决方案如下

@Slf4j
@Configuration
public class BeanPostProcessorConfig {


    @Bean
    public BeanPostProcessor springfoxHandlerProviderBeanPostProcessor() {
        return new BeanPostProcessor() {
            @Override
            public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
                if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider) {
                    customizeSpringfoxHandlerMappings(getHandlerMappings(bean));
                }
                return bean;
            }


            private <T extends RequestMappingInfoHandlerMapping> void customizeSpringfoxHandlerMappings(List<T> mappings) {
                List<T> copy = mappings.stream()
                        .filter(mapping -> mapping.getPatternParser() == null)
                        .collect(Collectors.toList());
                mappings.clear();
                mappings.addAll(copy);
            }


            @SuppressWarnings("unchecked")
            private List<RequestMappingInfoHandlerMapping> getHandlerMappings(Object bean) {
                try {
                    Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings");
                    field.setAccessible(true);
                    return (List<RequestMappingInfoHandlerMapping>) field.get(bean);
                } catch (IllegalArgumentException | IllegalAccessException e) {
                    throw new IllegalStateException(e);
                }
            }
        };
    }
}

官方给出的错误分析:

1.github.com/springfox/s…

2.错误详见:github.com/springfox/s…

3.官方最终建议是请选择SpringDoc 进行替换,swagger2在后续版本不在维护

源码分析:

requestMapping.getPatternsCondition()); 这行代码为空指针

requestMapping.getActivePatternsCondition()); 修改后

SpringBoot 2.7.18 集成 springdoc-openai-ui

GitHub地址:github.com/springdoc/s…

官方文档:springdoc.org/

添加依赖

<dependency>
       <groupId>org.springdoc</groupId>
       <artifactId>springdoc-openapi-ui</artifactId>
       <version>1.7.0</version>
 </dependency>

根据自身需求修改默认访问地址(如不需要跳过)

# swagger-ui 默认地址为 /swagger-ui.html
springdoc:
  swagger-ui:
    path: /swagger-ui.html

更多属性参考:springdoc.org/#swagger-ui…

# springdoc 默认访问地址就是 /v3/api-docs
springdoc.api-docs.path=/v3/api-docs

更多属性参考:springdoc.org/#properties

配置web静态资源路径注册

ps: 如果没有extends WebMvcConfigurationSupport 可以忽略

/**
     * 注册自定义拦截器
     *
     * @param registry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(interceptor)
                .excludePathPatterns("/swagger-ui/**", "/v2/**", "/error/**"); // 不拦截 swagger 访问路径
    }


    /**
     * 发现如果继承了WebMvcConfigurationSupport, 需要重新指定静态资源
     * 注册自定义静态资源
     *
     * @param registry
     */
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/swagger-ui/**")
                .addResourceLocations("classpath:/META-INF/resources/webjars/swagger-ui/4.18.2/");
        super.addResourceHandlers(registry);
    }

公众号“程序猿的游戏开源工具密圈”(ID:gh_cc865e4c536b)