springboot集成swagger2出现404解决方案汇总

简介: springboot集成swagger2出现404解决方案汇总

springboot整合swagger2:

1、依赖包:

<dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.7.0</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.7.0</version>
        </dependency>
        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>swagger-bootstrap-ui</artifactId>
            <version>1.8.3</version>
        </dependency>

image.gif

2、SwaggerConfig配置:

import io.swagger.annotations.Api;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.*;
import org.springframework.web.util.UriComponentsBuilder;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Parameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.paths.AbstractPathProvider;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import java.util.ArrayList;
import java.util.List;
import static com.google.common.base.Strings.isNullOrEmpty;
import static springfox.documentation.spring.web.paths.Paths.removeAdjacentForwardSlashes;
@Configuration
@EnableSwagger2
@Slf4j
@EnableConfigurationProperties(SwaggerYaml.class)
public class SwaggerConfig implements WebMvcConfigurer {
    private SwaggerYaml swaggerYaml;
    public SwaggerConfig(SwaggerYaml swaggerYaml) {
        this.swaggerYaml = swaggerYaml;
    }
    @Bean
    @ConditionalOnProperty(prefix = "swagger", name = "enable", havingValue = "true")
    public Docket api() {
        log.info("swagger配置信息初始化");
        return new Docket(DocumentationType.SWAGGER_2)
                .forCodeGeneration(true)
                .pathProvider(new CustRelativePathProvider())
                .select()
//                .apis(RequestHandlerSelectors.any())  //显示所有类
                //只显示添加@Api注解的类
                .apis(RequestHandlerSelectors.withClassAnnotation(Api.class))
                .paths(PathSelectors.any())
                .build()
                .apiInfo(apiInfo()).globalOperationParameters(parameters());
    }
    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title(swaggerYaml.getTitle())
                .description(swaggerYaml.getDescription())
                .version(swaggerYaml.getVersion())
                .termsOfServiceUrl(swaggerYaml.getTermsOfServiceUrl())
                .build();
    }
    /**
     * 全局参数。 接口请求header中的token。 required false非必须
     * @return
     */
    private List<Parameter> parameters(){
        List<Parameter> params = new ArrayList<>();
        params.add(
                new ParameterBuilder()
                        .name("token")
                        .description("请求令牌")
                        .modelRef(new ModelRef("String"))
                        .parameterType("header")
                        .required(false)
                        .build());
        return params;
    }
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");
        registry.addResourceHandler("/swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("/doc.html").addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
        super.addResourceHandlers(registry);
    }
    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }
    /**
     * 重新pathprovider,给所有url添加后缀
     */
    public class CustRelativePathProvider extends AbstractPathProvider {
        public static final String ROOT = "/";
        @Override
        public String getOperationPath(String operationPath) {
            UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromPath("/");
            String uri = removeAdjacentForwardSlashes(uriComponentsBuilder.path(operationPath).build().toString());
            return uri + ".jhtml";
        }
        @Override
        protected String applicationPath() {
            return isNullOrEmpty(swaggerYaml.getContextPath()) ? ROOT : swaggerYaml.getContextPath();
        }
        @Override
        protected String getDocumentationPath() {
            return ROOT;
        }
    }
}

image.gif

3、SpringApplication.run(xxx.class, args)。

由于我使用了swagger第三方ui:swagger-bootstrap-ui,所以我直接访问url:http://localhost:8080/doc.html。结果:

image.png

开始百度搜索解决大法:

基本网上说的解决方法不都是静态资源映射的问题,解决方式为:

@Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");
        registry.addResourceHandler("/swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("/doc.html").addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
        super.addResourceHandlers(registry);
    }
    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }

image.gif

但是我无效,而且我在springmvc上使用过swagger,自信配置应该是不缺。哈哈哈。

还有一个解决方式为:在application.yml上添加static资源映射,spring.resource.static-location:classpath:/resource/...等等类似,也无效。

最终:无意间在idea全局搜索WebMvcConfigurer发现自己曾经写的一个跨域的拦截器配置CrossOriginConfig同样实现了WebMvcConfigurer。于是抱着死马当活马的态度,将这个注释后,重启,发现熟悉的页面出来了:

image.png

于是百度了一圈,网上给出的解释大概为:springboot默认静态资源路径为

    • classpath:/META-INF/resources
    • classpath:/resources
    • classpath:/static
    • classpath:/public

    这也就是为什么我们前面要添加资源文件映射到swagger-ui.html的原因吧, 个人理解。

    然后,我们自己在容器中装配了自己的bean,springboot就不会帮我们自动装配(大概意思就是,CrossOriginConfig生成的baen把springboot的bean给覆盖了,菜鸟的白话理解,如有误,请指出

    这里更改后的两个配置,这里我是将CrossOriginConfig作为基类,供其他地方继承使用(当然,你可以直接在swaggerConfig中重写addCorsMappings也是一样的道理。):

    import org.springframework.web.servlet.config.annotation.CorsRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    /**
     * 处理跨域请求
     *
     * 这里不自动装配,作为基类提供子类继承。避免重复装配,导致bean被覆盖。(如被覆盖后,swagger-ui出现404)
     *
     * @author asus
     * @date 2020/4/8
     */
    //@Configuration
    public class CrossOriginConfig implements WebMvcConfigurer {
        @Override
        public void addCorsMappings(CorsRegistry registry) {
            registry.addMapping("/**").allowedOrigins("*")
                    .allowedMethods("POST", "GET", "PUT", "OPTIONS", "DELETE")
                    .maxAge(3600)
                    .allowCredentials(true);
        }
    }

    image.gif

    import com.kuria.config.cross.CrossOriginConfig;
    import io.swagger.annotations.Api;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
    import org.springframework.boot.context.properties.EnableConfigurationProperties;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.*;
    import org.springframework.web.util.UriComponentsBuilder;
    import springfox.documentation.builders.ApiInfoBuilder;
    import springfox.documentation.builders.ParameterBuilder;
    import springfox.documentation.builders.PathSelectors;
    import springfox.documentation.builders.RequestHandlerSelectors;
    import springfox.documentation.schema.ModelRef;
    import springfox.documentation.service.ApiInfo;
    import springfox.documentation.service.Parameter;
    import springfox.documentation.spi.DocumentationType;
    import springfox.documentation.spring.web.paths.AbstractPathProvider;
    import springfox.documentation.spring.web.plugins.Docket;
    import springfox.documentation.swagger2.annotations.EnableSwagger2;
    import java.util.ArrayList;
    import java.util.List;
    import static com.google.common.base.Strings.isNullOrEmpty;
    import static springfox.documentation.spring.web.paths.Paths.removeAdjacentForwardSlashes;
    /**
     * swagger2 配置
     * @ConditionalOnProperty 用于控制当前config是否生效
     * swagger-open: true生效,false则关闭
     *
     * 继承CrossOriginConfig, 原因CrossOriginConfig配置跨域拦截处理,为了避免bean被覆盖,导致swagger-ui 404.这里不重复装配
     * 由CrossOriginConfig 作为基类,提供继承。当然,如果SwaggerConfig重写addCorsMappings也是可以的。
     *
     * @author pengyh
     * @date 2020/4/10
     */
    @Configuration
    @EnableSwagger2
    @Slf4j
    @EnableConfigurationProperties(SwaggerYaml.class)
    public class SwaggerConfig extends CrossOriginConfig {
        private SwaggerYaml swaggerYaml;
        public SwaggerConfig(SwaggerYaml swaggerYaml) {
            this.swaggerYaml = swaggerYaml;
        }
        @Bean
        @ConditionalOnProperty(prefix = "swagger", name = "enable", havingValue = "true")
        public Docket api() {
            log.info("swagger配置信息初始化");
            return new Docket(DocumentationType.SWAGGER_2)
                    .forCodeGeneration(true)
                    .pathProvider(new CustRelativePathProvider())
                    .select()
    //                .apis(RequestHandlerSelectors.any())  //显示所有类
    //                .apis(RequestHandlerSelectors.basePackage("com.kuria.site.controller"))
                    //只显示添加@Api注解的类
                    .apis(RequestHandlerSelectors.withClassAnnotation(Api.class))
                    .paths(PathSelectors.any())
                    .build()
                    .apiInfo(apiInfo()).globalOperationParameters(parameters());
        }
        private ApiInfo apiInfo() {
            return new ApiInfoBuilder()
                    .title(swaggerYaml.getTitle())
                    .description(swaggerYaml.getDescription())
                    .version(swaggerYaml.getVersion())
                    .termsOfServiceUrl(swaggerYaml.getTermsOfServiceUrl())
                    .build();
        }
        /**
         * 全局参数。 接口请求header中的token。 required false非必须
         * @return
         */
        private List<Parameter> parameters(){
            List<Parameter> params = new ArrayList<>();
            params.add(
                    new ParameterBuilder()
                            .name("token")
                            .description("请求令牌")
                            .modelRef(new ModelRef("String"))
                            .parameterType("header")
                            .required(false)
                            .build());
            return params;
        }
        @Override
        public void addResourceHandlers(ResourceHandlerRegistry registry) {
            registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");
            registry.addResourceHandler("/swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
            registry.addResourceHandler("/doc.html").addResourceLocations("classpath:/META-INF/resources/");
            registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
            super.addResourceHandlers(registry);
        }
        @Override
        public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
            configurer.enable();
        }
        /**
         * 重新pathprovider,给所有url添加后缀
         */
        public class CustRelativePathProvider extends AbstractPathProvider {
            public static final String ROOT = "/";
            @Override
            public String getOperationPath(String operationPath) {
                UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromPath("/");
                String uri = removeAdjacentForwardSlashes(uriComponentsBuilder.path(operationPath).build().toString());
                return uri + ".jhtml";
            }
            @Override
            protected String applicationPath() {
                return isNullOrEmpty(swaggerYaml.getContextPath()) ? ROOT : swaggerYaml.getContextPath();
            }
            @Override
            protected String getDocumentationPath() {
                return ROOT;
            }
        }
    }

    image.gif

    问题解决。

    备注:测试发现,如果SwaggerConfig继承WebMvcConfigurationSupport类。而CrossOriginConfig实现WebMvcConfigurer接口,也是可以正常使用的, 不知道为何,待研究,也可能是我本地缓存,没有认真试验过。

    踩坑之路,备注,方便日后查阅。

    相关文章
    |
    20天前
    |
    XML Java API
    Spring Boot集成MinIO
    本文介绍了如何在Spring Boot项目中集成MinIO,一个高性能的分布式对象存储服务。主要步骤包括:引入MinIO依赖、配置MinIO属性、创建MinIO配置类和服务类、使用服务类实现文件上传和下载功能,以及运行应用进行测试。通过这些步骤,可以轻松地在项目中使用MinIO的对象存储功能。
    |
    22天前
    |
    消息中间件 Java Kafka
    什么是Apache Kafka?如何将其与Spring Boot集成?
    什么是Apache Kafka?如何将其与Spring Boot集成?
    55 5
    |
    25天前
    |
    Java 测试技术 API
    详解Swagger:Spring Boot中的API文档生成与测试工具
    详解Swagger:Spring Boot中的API文档生成与测试工具
    35 4
    |
    25天前
    |
    消息中间件 Java Kafka
    Spring Boot 与 Apache Kafka 集成详解:构建高效消息驱动应用
    Spring Boot 与 Apache Kafka 集成详解:构建高效消息驱动应用
    37 1
    |
    27天前
    |
    存储 Prometheus 运维
    在云原生环境中,阿里云ARMS与Prometheus的集成提供了强大的应用实时监控解决方案
    在云原生环境中,阿里云ARMS与Prometheus的集成提供了强大的应用实时监控解决方案。该集成结合了ARMS的基础设施监控能力和Prometheus的灵活配置及社区支持,实现了全面、精准的系统状态、性能和错误监控,提升了应用的稳定性和管理效率。通过统一的数据视图和高级查询功能,帮助企业有效应对云原生挑战,促进业务的持续发展。
    34 3
    |
    1月前
    |
    XML Java 数据库连接
    SpringBoot集成Flowable:打造强大的工作流管理系统
    在企业级应用开发中,工作流管理是一个核心组件,它能够帮助我们定义、执行和管理业务流程。Flowable是一个开源的工作流和业务流程管理(BPM)平台,它提供了强大的工作流引擎和建模工具。结合SpringBoot,我们可以快速构建一个高效、灵活的工作流管理系统。本文将探讨如何将Flowable集成到SpringBoot应用中,并展示其强大的功能。
    193 1
    |
    25天前
    |
    消息中间件 监控 Java
    您是否已集成 Spring Boot 与 ActiveMQ?
    您是否已集成 Spring Boot 与 ActiveMQ?
    49 0
    |
    1月前
    |
    JSON Java API
    springboot集成ElasticSearch使用completion实现补全功能
    springboot集成ElasticSearch使用completion实现补全功能
    40 1
    |
    1月前
    |
    XML 存储 Java
    SpringBoot集成Flowable:构建强大的工作流引擎
    在企业级应用开发中,工作流管理是核心功能之一。Flowable是一个开源的工作流引擎,它提供了BPMN 2.0规范的实现,并且与SpringBoot框架完美集成。本文将探讨如何使用SpringBoot和Flowable构建一个强大的工作流引擎,并分享一些实践技巧。
    134 0
    |
    2月前
    |
    前端开发 Java 程序员
    springboot 学习十五:Spring Boot 优雅的集成Swagger2、Knife4j
    这篇文章是关于如何在Spring Boot项目中集成Swagger2和Knife4j来生成和美化API接口文档的详细教程。
    194 1