共计 7149 个字符,预计需要花费 18 分钟才能阅读完成。
最近同事问我,spring boot 集成了 swagger,但是在使用拦截器的时候遇到了问题,页面无法访问。经过研究解决了这个问题。
配置问题解决
集成 swagger 就不啰嗦了,网上到处都是,直接看配置。
同事从网上找到的配置:
import com.xxx.xxxx.xxx.xxx.LoginInterceptor;
import com.fasterxml.classmate.TypeResolver;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.ResponseEntity;
import org.springframework.web.context.request.async.DeferredResult;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.schema.WildcardType;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import java.util.Collections;
import static springfox.documentation.schema.AlternateTypeRules.newRule;
@Configuration
@EnableSwagger2
public class Swagger2Config extends WebMvcConfigurationSupport {
@Autowired
private TypeResolver typeResolver;
@Bean
public Docket productApi() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage(“com.xxx.xxx.controller”))
.paths(PathSelectors.any())
.build()
.apiInfo(metaData())
.alternateTypeRules(// 自定义规则,如果遇到 DeferredResult,则把泛型类转成 json
newRule(typeResolver.resolve(DeferredResult.class,
typeResolver.resolve(ResponseEntity.class, WildcardType.class)),
typeResolver.resolve(WildcardType.class)))
;
}
private ApiInfo metaData() {
return new ApiInfoBuilder()
.title(“ 通用服务 APIs”)
/*.description(“\”REST API for Online Store\””)*/
.version(“1.0.0”)
/* .license(“Apache License Version 2.0”)
.licenseUrl(“https://www.apache.org/licenses/LICENSE-2.0\””)*/
.contact(new Contact(“ 易保科技 ”, “”, “mail@mail”))
.build();
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler(“swagger-ui.html”)
.addResourceLocations(“classpath:/META-INF/resources/”);
registry.addResourceHandler(“/webjars/**”)
.addResourceLocations(“classpath:/META-INF/resources/webjars/”);
super.addResourceHandlers(registry);
}
}
这是他从网上找到的拦截器的配置:
@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor())
// addPathPatterns 用于添加拦截规则,先把所有路径都加入拦截,再一个个排除
.addPathPatterns(“/**”)
.excludePathPatterns(Collections.singletonList(“/swagger-ui.html”));
super.addInterceptors(registry);
}
}
现在的测试结果就是,打开 http://host:port/path/swagger-ui.html,就是一个空白页面,无法使用,现在要解决的就是这个问题。
打开谷歌浏览器的调试控制台,查看 network,如图:可以明显看出,页面加载数据的时候,并没有报什么错误,只是加载的资源都被拦截器拦截了,无法加载资源,可想而知,资源都被拦截器拦截了。
我分析了一下,加载资源的路径,修改了一下拦截器资源配置:
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginInterceptor)
// addPathPatterns 用于添加拦截规则,先把所有路径都加入拦截,再一个个排除
.addPathPatterns(“/**”)
.excludePathPatterns(“/swagger-ui.html”)
.excludePathPatterns(“/swagger-resources/**”)
.excludePathPatterns(“/error”)
.excludePathPatterns(“/webjars/**”);
}
另外两个类实际是同一个作用,所以合并两个类:
@Configuration
@EnableSwagger2
public class TestMVCConfig extends WebMvcConfigurationSupport {
@Resource
private TypeResolver typeResolver;
@Resource
private LoginInterceptor loginInterceptor;
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler(“swagger-ui.html”)
.addResourceLocations(“classpath:/META-INF/resources/”);
registry.addResourceHandler(“/webjars/**”)
.addResourceLocations(“classpath:/META-INF/resources/webjars/”);
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginInterceptor)
// addPathPatterns 用于添加拦截规则,先把所有路径都加入拦截,再一个个排除
.addPathPatterns(“/**”)
.excludePathPatterns(“/swagger-ui.html”)
.excludePathPatterns(“/swagger-resources/**”)
.excludePathPatterns(“/error”)
.excludePathPatterns(“/webjars/**”);
}
@Bean
public Docket productApi() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage(“com.xxx.xxx.controller”))
.paths(PathSelectors.any())
.build()
.apiInfo(metaData())
.alternateTypeRules(// 自定义规则,如果遇到 DeferredResult,则把泛型类转成 json
newRule(typeResolver.resolve(DeferredResult.class,
typeResolver.resolve(ResponseEntity.class, WildcardType.class)),
typeResolver.resolve(WildcardType.class)))
;
}
private ApiInfo metaData() {
return new ApiInfoBuilder()
.title(“ 通用服务 APIs”)
/*.description(“\”REST API for Online Store\””)*/
.version(“1.0.0”)
/* .license(“Apache License Version 2.0”)
.licenseUrl(“https://www.apache.org/licenses/LICENSE-2.0\””)*/
.contact(new Contact(“ 易保科技 ”, “”, “mail@mail”))
.build();
}
}
这样就没有问题了。
另外的解决方案
网上还有另外一种说法,可以实现 WebMvcConfigurer 接口,代码如下:
@Configuration
@EnableWebMvc
@EnableSwagger2
public class WebMVCConfig implements WebMvcConfigurer{
@Resource
private TypeResolver typeResolver;
@Resource
private LoginInterceptor loginInterceptor;
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler(“swagger-ui.html”)
.addResourceLocations(“classpath:/META-INF/resources/”);
registry.addResourceHandler(“/webjars/**”)
.addResourceLocations(“classpath:/META-INF/resources/webjars/”);
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
List<String> excludePathPatterns = new ArrayList<>();
excludePathPatterns.add(“/swagger-ui.html”);
excludePathPatterns.add(“/swagger-resources/**”);
excludePathPatterns.add(“/error”);
excludePathPatterns.add(“/webjars/**”);
// addPathPatterns 用于添加拦截规则,先把所有路径都加入拦截,再一个个排除
registry.addInterceptor(loginInterceptor)
.addPathPatterns(“/**”)
.excludePathPatterns(excludePathPatterns);
}
@Bean
public Docket productApi() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage(“com.xx.sss.controller”))
.paths(PathSelectors.any())
.build()
.apiInfo(metaData())
.alternateTypeRules(// 自定义规则,如果遇到 DeferredResult,则把泛型类转成 json
newRule(typeResolver.resolve(DeferredResult.class,
typeResolver.resolve(ResponseEntity.class, WildcardType.class)),
typeResolver.resolve(WildcardType.class)))
;
}
private ApiInfo metaData() {
return new ApiInfoBuilder()
.title(“ 通用服务 APIs”)
/*.description(“\”REST API for Online Store\””)*/
.version(“1.0.0”)
/* .license(“Apache License Version 2.0”)
.licenseUrl(“https://www.apache.org/licenses/LICENSE-2.0\””)*/
.contact(new Contact(“ 易保科技 ”, “”, “mail@mail”))
.build();
}
}
但是这种配置想要生效,必须加 @EnableWebMvc 注解,不然不起作用。为什么?官方源码注释:
/**
* Defines callback methods to customize the Java-based configuration for
* Spring MVC enabled via {@code @EnableWebMvc}.
*
* <p>{@code @EnableWebMvc}-annotated configuration classes may implement
* this interface to be called back and given a chance to customize the
* default configuration.
*
通过 @enableWebMVC 启用 Spring MVC,自定义基于 Java config 定义回调方法。
@EnableWebMVC 带注释的配置类可以实现这个接口自定义默认配置。
当然如果你觉得自己的配置没问题,但是仍然不起作用,这时候改怎么办?请按照一下步骤 debug:1、找到 InterceptorRegistration 类;2、找到 addInterceptor 方法和 excludePathPatterns 方法,打上断点;3、debug 模式启动项目;
如果没有进入断点,那就说明你的配置根本没有起到作用,看看注解是否没写。
如果进入了断点,就要看看断点处传进来的参数是否是你配置的参数,不是那就是有问题,这时候再根据参数查找问题。
这样基本就能解决问题了。
总结
网上很多东西都是抄来抄去,也不知道有没有验证,让很多人摸不着头脑。