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

2024-01-25 10:50:47 浏览数 (2)

springboot整合swagger2:

1、依赖包:

代码语言:javascript复制
<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>

2、SwaggerConfig配置:

代码语言:javascript复制
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;
        }
    }


}

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

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

开始百度搜索解决大法:

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

代码语言:javascript复制
@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();
    }

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

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

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

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

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

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

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

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

代码语言:javascript复制
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);
    }
}
代码语言:javascript复制
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;
        }
    }


}

问题解决。

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

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

0 人点赞