spring zuul转发问题
spring zuul项目中的RestController api成功调用,但是存在部分接口调用了PostFilter,但是其它接口不执行PostFilter
原因
zuul:
prefix: /v1
routes:
new-platform-env:
sensitiveHeaders: Access-Control-Allow-Origin,Access-Control-Allow-Methods
path: /v1/**
url: http://trade-application-gateway:8080
stripPrefix: false
new-platform-env2:
sensitiveHeaders: Access-Control-Allow-Origin,Access-Control-Allow-Methods
path: /v2/**
url: http://trade-application-gateway:8080
stripPrefix: false
出现上述问题的原因是在spring zuul配置中,zuul的前缀为v1
,如果routes中配置了以/v1
为前缀的路由,则会出现该问题
分析
DispatcherServlet.doDispatch()
中getHandler(processedRequest);会循环handlerMappings和request进行判断- handlerMappings 是 HandlerMapping实例集合,HandlerMapping是一个用于实现请求和处理类映射的接口
- handlerMapping存在以下实现
- SimpleUrlHandlerMapping
- ZuulHandlerMapping
- EndpointHandlerMapping
- RequestMappingHandlerMapping
- BeanNameUrlHandlerMapping
- SimpleUrlHandlerMapping
- WebMvcConfigurationSupport$EmptyHandlerMapping
- WebMvcConfigurationSupport$EmptyHandlerMapping
- WebMvcAutoConfiguration$WelcomePageHandlerMapping
- handlerMapping中存在方法getHandler(HttpServletRequest request),该方法通过request匹配对应的HandlerExecutionChain,getHandler()将匹配到的第一个返回
- route路由是通过ZuulHandlerMapping匹配到的,而普通的RestController接口是通过RequestMappingHandlerMapping匹配到的HandlerExecutionChain
AbstractHandlerMapping.getHandler(HttpServletRequest request)
中会根据request匹配对应的HandlerExecutionChain- 在getHandler()方法中,会调用
getHandlerInternal(request);
,此处使用模板模式,该方法是一个抽象方法;ZuulHandlerMapping是调用的AbstractUrlHandlerMapping,而RequestMappingHandlerMapping是调用的AbstractHandlerMethodMapping的getHandlerInternal(request);
方法
- 在getHandler()方法中,会调用
ZuulHandlerMapping.getHandlerInternal(request)
的执行过程- ZuulHandlerMapping通过
AbstractUrlHandlerMapping.getHandlerInternal(request)
最终会调用ZuulHandlerMapping.lookupHandler(String urlPath, HttpServletRequest request) - 在AbstractUrlHandlerMapping存在全局变量handlerMap
- 在handlerMap中保存有Zuul配置的路由映射,见图1
- 如图1所示,由于zuul前端及route前端均配置的是v1,但是却只展示了一个v1,故可匹配上
- 然后将匹配到的handler返回
图1
- ZuulHandlerMapping通过
RequestMappingHandlerMapping.getHandlerInternal(request)
的执行过程- 在该方法中会调用
lookupHandlerMethod(lookupPath, request)
- 在lookupHandlerMethod方法中调用
this.mappingRegistry.getMappingsByUrl(lookupPath);
该方法会获取请求url匹配的注册映射请求http信息,如http method,param等 - 最终会匹配到对应的HandlerMethod,在该实例中封装有执行的类及方法等信息
- 在该方法中会调用
- 在上述不同HandlerMapping返回handler后,会通过handler生成对应的HandlerExecutionChain
- 然后会执行对应的handler,即
v1/**
对应的是ZuulController,然后执行ZuulServlet.service()方法;而test
无对应的route,则执行对应的Controller - ZuulServlet.service()方法中,会执行pre,route及post等过滤器
- 在route时,由于实际并没有对应的
/v1/**
路由,故在类PreDecorationFilter中会提示No route found for uri: /v1/test
,并且会在ctx中set forward.to - 然后会重新DispatcherServlet,这次由于在ctx.containsKey("forward.to")为true,所以在ZuulHandlerMapping.lookupHandler时直接返回null,故下一次直接走RestController api
- 在route时,由于实际并没有对应的
值得注意的是,如果请求是
/v1/test
,实际的执行过程为filter->ZuulServlet->PreFilter->发现无匹配的路由,重新分发->filter->controller->filter->响应数据->postfilter->zuulServlet->filter
所以在postfilter中是无法获取到响应状态及body的,并且在postfilter中write message,是无法真正响应到客户端的