背景
- 最近项目部署遇到这样一个问题:paas平台部署项目,健康检查接口固定路径为:/actuator/health,但该项目为每个请求路径加上全局的前缀,如:/api/actuator/health,所以paas平台规范不通过导致部署失败,可能很多人会想到,直接去掉全局/api 即可,但是否想过该项目很多前端页面、第三方平台已经都对接了该项目的接口,如去掉全局前缀,那么势必需要所有调用链头部请求也做修改,时间、成本都比较大,所以是否有一种方式请求的原始路径保持不变,重定向到新路径。
修改过程
- 先去掉 全局前缀 application.yaml文件中server.servlet.context-path = /api ,
- 方式一:
编写一个配置类,重新新增前缀/api,这样就可以指定不同的类或者路径下不同的前缀。
@Configuration
public class MyWebMvcConfigurationSupport extends WebMvcConfigurationSupport {
@Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
return new MyCustomRequestMappingHandlerMapping();
}
private class MyCustomRequestMappingHandlerMapping extends RequestMappingHandlerMapping {
private final static String PREFIX = "com.xx";
private final static String EXCLUDE_CLASS = "com.xx.xxx.controller.HealthController";
public MyCustomRequestMappingHandlerMapping() {
super();
}
@Override
protected void registerHandlerMethod(Object handler, Method method, RequestMappingInfo mapping) {
Class<?> beanType = method.getDeclaringClass();
//排除健康检查类
if (!Objects.equals(beanType.getName(), EXCLUDE_CLASS) && beanType.getName().startsWith(PREFIX )) {
RestController myRestAnnotation = beanType.getAnnotation(RestController.class);
if (myRestAnnotation != null) {
PatternsRequestCondition oldPattern = mapping.getPatternsCondition();
PatternsRequestCondition apiPattern = new PatternsRequestCondition("/api/");
//合并旧的url和新的
PatternsRequestCondition updatedFinalPattern = apiPattern.combine(oldPattern);
mapping = new RequestMappingInfo(
mapping.getName(),
updatedFinalPattern,
mapping.getMethodsCondition(),
mapping.getParamsCondition(),
mapping.getHeadersCondition(),
mapping.getConsumesCondition(),
mapping.getProducesCondition(),
mapping.getCustomCondition()
);
}
}
super.registerHandlerMethod(handler, method, mapping);
}
}
}
- 方式二:编写一个MVC配置类
@Configuration
public class PluginConfig implements WebMvcConfigurer {
public static final String PREFIX = "/api";
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.addPathPrefix(PREFIX, c -> c.isAnnotationPresent(RestController.class));
}
- 方式三:编写一个过滤器重定向
@Order(0)
@Component
public class UrlFilter extends OncePerRequestFilter {
/**
*
* 排除api关键字
* @param request
* @param response
* @param filterChain
* @throws ServletException
* @throws IOException
*/
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String path = request.getRequestURI();
path = "/api/"+ path
request.getRequestDispatcher(path).forward(request, response);
}
}
最后附一张过滤器、拦截器、servlet请求顺序的关系图,以上案例,不能使用拦截器,因为需要在经过servlet容器之前做定向处理。
请求过程:请求->Listener->ServletContainer->Filter->Servlet->Interceptor->Controller
SpringBoot中可查看这三个类对Filter、Servlet、Listener进行操作ServletListenerRegistrationBean、FilterRegistrationBean、ServletRegistrationBean