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接口,也是可以正常使用的, 不知道为何,待研究,也可能是我本地缓存,没有认真试验过。

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

    相关文章
    |
    4天前
    |
    Java 数据库连接 数据安全/隐私保护
    springBoot集成token认证,最全Java面试知识点梳理
    springBoot集成token认证,最全Java面试知识点梳理
    |
    5天前
    |
    NoSQL Java MongoDB
    【MongoDB 专栏】MongoDB 与 Spring Boot 的集成实践
    【5月更文挑战第11天】本文介绍了如何将非关系型数据库MongoDB与Spring Boot框架集成,以实现高效灵活的数据管理。Spring Boot简化了Spring应用的构建和部署,MongoDB则以其对灵活数据结构的处理能力受到青睐。集成步骤包括:添加MongoDB依赖、配置连接信息、创建数据访问对象(DAO)以及进行数据操作。通过这种方式,开发者可以充分利用两者优势,应对各种数据需求。在实际应用中,结合微服务架构等技术,可以构建高性能、可扩展的系统。掌握MongoDB与Spring Boot集成对于提升开发效率和项目质量至关重要,未来有望在更多领域得到广泛应用。
    【MongoDB 专栏】MongoDB 与 Spring Boot 的集成实践
    |
    5天前
    |
    消息中间件 JSON Java
    RabbitMQ的springboot项目集成使用-01
    RabbitMQ的springboot项目集成使用-01
    |
    5天前
    |
    搜索推荐 Java 数据库
    springboot集成ElasticSearch的具体操作(系统全文检索)
    springboot集成ElasticSearch的具体操作(系统全文检索)
    |
    5天前
    |
    网络协议 Java Spring
    Springboot 集成websocket
    Springboot 集成websocket
    12 0
    |
    5天前
    |
    安全 Java 数据库连接
    在IntelliJ IDEA中通过Spring Boot集成达梦数据库:从入门到精通
    在IntelliJ IDEA中通过Spring Boot集成达梦数据库:从入门到精通
    |
    5天前
    |
    缓存 NoSQL Java
    springboot业务开发--springboot集成redis解决缓存雪崩穿透问题
    该文介绍了缓存使用中可能出现的三个问题及解决方案:缓存穿透、缓存击穿和缓存雪崩。为防止缓存穿透,可校验请求数据并缓存空值;缓存击穿可采用限流、热点数据预加载或加锁策略;缓存雪崩则需避免同一时间大量缓存失效,可设置随机过期时间。文章还提及了Spring Boot中Redis缓存的配置,包括缓存null值、使用前缀和自定义过期时间,并提供了改造代码以实现缓存到期时间的个性化设置。
    |
    5天前
    |
    Java Docker 容器
    SpringBoot项目集成XXL-job
    SpringBoot项目集成XXL-job
    |
    5天前
    |
    Java 关系型数据库 数据库
    【SpringBoot系列】微服务集成Flyway
    【4月更文挑战第7天】SpringBoot微服务集成Flyway
    42 1
    【SpringBoot系列】微服务集成Flyway
    |
    5天前
    |
    前端开发 安全 Java
    springboot集成springdoc-openapi(模拟前端请求)
    springboot集成springdoc-openapi(模拟前端请求)

    热门文章

    最新文章