SpringBoot配置Swagger/访问静态资源失败
🌤️

SpringBoot配置Swagger/访问静态资源失败

Published
May 1, 2022
Description
SpringBoot集成Swagger3.0.0报错,浏览器中无法访问;static文件夹下的静态资源无法直接访问,在yaml中配置了相关设置还是走的动态请求。
Tags
SpringBoot
Swagger
Author
Property
 

1. 引言

在初次使用 SpringBoot 做项目时遇到了两个问题,第一个问题是 SpringBoot 在集成了 Swagger 无法正常启动项目,然后解决了这个错误后,在浏览器中还是无法访问到 Swagger 页面。第二个问题是 SpringBoot 访问静态资源时总是会走动态请求,访问到后端 Controller 去。在通过不断查找资料和具体实践之后,成功解决了这两个问题,在此将其记录下来,希望能为后来者提供些许帮助。

2. Swgger 页面无法访问

SpringBoot 版本为 2.6.4 ,Swagger pom.xml依赖为 3.0.0
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.6.4</version> <relativePath/> <!-- lookup parent from repository --> </parent> <!-- 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/io.springfox/springfox-swagger2 --> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>3.0.0</version> </dependency>
 
  1. 单独为 Swagger 写一个配置类
@Configuration @EnableSwagger2 public class SwaggerConfig { //配置Swagger的Docket的Bean实例 @Bean public Docket createRestApi() { return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .select() //使其只扫描有 @RestController 注解的类 .apis(RequestHandlerSelectors.withClassAnnotation(RestController.class)) .build(); } //配置Swagger信息 private ApiInfo apiInfo() { return new ApiInfo( "API Document", "API文档", "v1.0", "", "Apache 2.0", "http://www.apache.org/licenses/LICENSE-2.0", new ArrayList<>() ); } }
之前写完这个配置类之后,启动项目会自动退出,在我写这篇文章时,虽然进行了多次尝试,但还是没能复现当时遇到的bug。如果你产生这个问题的话,可以在添加一个类
@Component public class SwaggerBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider) { List<RequestMappingInfoHandlerMapping> handlerMappings = getHandlerMappings(bean); customizeSpringfoxHandlerMappings(handlerMappings); } 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); } } }
与此同时,需要特别注意一下访问路径,在Swagger3.0.0中访问路径为 : http://localhost:8080/swagger-ui/index.html 如果没有配置访问路径就会产生这样的问题。
notion image
 
记得在配置一下静态资源访问路径,否则请求 swagger 会 404
@Configuration @EnableWebMvc public class WebMvcConfig implements WebMvcConfigurer { //配置静态资源处理拦截器,如果访问的路径是 addResourceHandler() 中的的话,那就映射到访问本地的 addResourceLocations() 的参数的路径中 @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/static/**","/swagger-ui/**") .addResourceLocations("classpath:/static/","classpath:/META-INF/resources/webjars/springfox-swagger-ui/") .resourceChain(false); } }
Swagger中的一些注解说明
@Api:修饰整个类,描述Controller的作用 @ApiOperation:描述一个类的一个方法,或者说一个接口 @ApiParam:单个参数描述 @ApiModel:用对象来接收参数 @ApiProperty:用对象接收参数时,描述对象的一个字段 @ApiResponse:HTTP响应其中1个描述 @ApiResponses:HTTP响应整体描述 @ApiIgnore:使用该注解忽略这个API @ApiError :发生错误返回的信息 @ApiImplicitParam:描述一个请求参数,可以配置参数的中文含义,还可以给参数设置默认值 @ApiImplicitParams:描述由多个 @ApiImplicitParam 注解的参数组成的请求参数列表
notion image
 
 

3. SpringBoot 访问静态资源

SpringBoot中静态资源放在 static、public、resources 目录下,在这些目录下,就可以在浏览器中直接访问这些资源,不用再通过 Controller 进行动态请求。
但经过尝试之后发现,当访问我项目下的static文件夹中的静态资源总是会进行动态请求
notion image
项目结构图
即使在 yaml 中配置静态资源访问路径后还是不起作用,一直会进行动态请求。
spring: mvc: static-path-pattern: "/static/**" web: resources: static-locations: classpath:/static/
 
在 springboot 的文档中,找到了出现这个问题的原因
notion image
 
 

1.1.5. Static Content

By default, Spring Boot serves static content from a directory called /static  (or /public  or /resources or  /META-INF/resources) in the classpath or from the root of the ServletContext. It uses the ResourceHttpRequestHandler from Spring MVC so that you can modify that behavior by adding your own WebMvcConfigurer and overriding the addResourceHandlers method.
默认情况下,Spring Boot 从类路径中名为/static(或/public/resources/META-INF/resources)的目录或从ServletContext. 它使用ResourceHttpRequestHandler来自 Spring MVC 的方法,因此您可以通过添加自己的方法WebMvcConfigurer并覆盖该addResourceHandlers方法来修改该行为。
In a stand-alone web application, the default servlet from the container is also enabled and acts as a fallback, serving content from the root of the ServletContext if Spring decides not to handle it. Most of the time, this does not happen (unless you modify the default MVC configuration), because Spring can always handle requests through the DispatcherServlet.
在一个独立的web应用程序中,容器中的默认servlet也会被启用,并充当后备服务,如果Spring决定不处理它,则从ServletContext的根目录提供内容。大多数情况下,这种情况不会发生(除非修改默认的MVC配置),因为Spring总是可以通过DispatcherServlet处理请求。
By default, resources are mapped on /**, but you can tune that with the spring.mvc.static-path-pattern property. For instance, relocating all resources to /resources/** can be achieved as follows:
默认情况下,资源映射在 上/**,但您可以使用该spring.mvc.static-path-pattern属性对其进行调整。例如,将所有资源重新定位到/resources/**可以实现如下:
spring: mvc: static-path-pattern: "/resources/**"
 
因此需要自己去实现 WebMvcConfigurer 中的 addResourceHandlers 方法 从而配置静态资源访问路径,使其不再走动态请求。
@Configuration @EnableWebMvc public class WebMvcConfig implements WebMvcConfigurer { //配置静态资源处理拦截器,如果访问的路径是 addResourceHandler() 中的的话,那就映射到访问本地的 addResourceLocations() 的参数的路径中 @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/**").addResourceLocations("classpath:/static"); } }
可以看到图片可以正常访问了
notion image
 
如果将 addResourceHandlers 注释掉,重启项目,重新进行访问则会看到
notion image
idea的控制台将会显示
notion image
 
 

4. 拓展

WebMvcConfigurationSupport 和 WebMvcConfigurer 的区别
 
WebMvcConfigurationSupport :
notion image
WebMvcConfigurer :
notion image
可以看到,两个不同的类中有着相同的方法,而且方法的作用还是一样的。这个问题还需要细细探究,等研究明白再来补充。

5. 结语

到此,SpringBoot 的这两个问题就成功解决了,虽然我遇到的这个问题通过这个方法成功解决,但是人和人的悲欢并不尽相同,也许当你遇到同样的问题,通过以上方法还是没能解决的话。我的经验告诉我,还得多尝试,不断地修改、不断地测试。问题就是这么一步步解决掉的,而成功也将一步步的向你靠近。好了,如果你在此过程中有什么其他问题,可以在下方的评论区给我留言!
 
参考文章:

Loading Comments...