Spring中如何监听无@RestController及未映射请求?用于网关转发
嘿,你的需求其实很适合用Spring的过滤器(Filter)或者HandlerInterceptor来实现——比写一个通配符控制器优雅太多,而且能真正拦截所有到达服务的请求,包括那些没有对应@RequestMapping的请求。下面给你详细拆解几个可行的方案:
方案一:使用Servlet Filter(最推荐)
Servlet Filter是在DispatcherServlet之前执行的,属于Web容器层面的拦截器,不管请求有没有对应的Spring控制器映射,都会被拦截,完全符合你网关转发的需求。
实现步骤:
- 编写自定义Filter类,用
@WebFilter指定拦截所有路径:
import javax.servlet.*; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import java.io.IOException; @WebFilter(urlPatterns = "/*") public class RequestForwardFilter implements Filter { // 注入你用来查询下游映射的服务(从数据库取配置) // private DownstreamMappingService mappingService; @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) servletRequest; String requestUri = request.getRequestURI(); // 1. 查询数据库,获取对应的下游URI // String downstreamUri = mappingService.getDownstreamUri(requestUri); // 2. 如果找到映射,就处理转发逻辑 if (downstreamUri != null) { // 这里分两种情况: // - 下游是同容器内的服务:用RequestDispatcher转发 // servletRequest.getRequestDispatcher(downstreamUri).forward(servletRequest, servletResponse); // - 下游是外部服务:用WebClient/RestTemplate发起请求并返回结果 // String responseBody = WebClient.create() // .method(request.getMethod()) // .uri(downstreamUri) // .headers(headers -> headers.putAll(request.getHeaderNames().stream() // .collect(Collectors.toMap(h -> h, request::getHeader)))) // .bodyValue(request.getInputStream()) // .retrieve() // .bodyToMono(String.class) // .block(); // servletResponse.getWriter().write(responseBody); return; // 处理完转发,直接结束流程,不走后续过滤器和Spring MVC } // 没有找到映射,继续走原流程(交给Spring MVC处理) filterChain.doFilter(servletRequest, servletResponse); } }
- 在Spring Boot启动类上添加
@ServletComponentScan,让Spring扫描到你的Filter:
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.web.servlet.ServletComponentScan; @SpringBootApplication @ServletComponentScan public class YourApplication { public static void main(String[] args) { SpringApplication.run(YourApplication.class, args); } }
方案二:使用Spring MVC HandlerInterceptor
HandlerInterceptor属于Spring MVC层面的拦截器,在DispatcherServlet之后、具体Handler执行前触发。它能拦截所有进入Spring MVC的请求,适合只关注业务请求的场景。
实现步骤:
- 编写自定义Interceptor类:
import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @Component public class RequestForwardInterceptor implements HandlerInterceptor { // 注入映射服务 // private DownstreamMappingService mappingService; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String requestUri = request.getRequestURI(); // 查询下游映射 // String downstreamUri = mappingService.getDownstreamUri(requestUri); if (downstreamUri != null) { // 同样可以用WebClient调用外部服务并返回结果 // 逻辑和Filter里的类似,这里不再重复 return false; // 返回false,阻止后续Handler执行 } // 没有映射,继续执行原流程 return true; } }
- 配置Interceptor使其生效:
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class WebMvcConfig implements WebMvcConfigurer { @Autowired private RequestForwardInterceptor requestForwardInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(requestForwardInterceptor) .addPathPatterns("/**"); // 拦截所有路径 } }
为什么不推荐你原来的通配符控制器?
用@RequestMapping("*")的方式有几个明显的问题:
- 优先级问题:Spring MVC会优先匹配更具体的
@RequestMapping,如果有其他控制器定义了更精确的路径,你的通配符控制器就不会被触发; - 范围有限:它只能拦截Spring MVC能识别的请求,比如静态资源、Actuator端点这类走特殊处理逻辑的请求,不会被这个控制器捕获;
- 不够灵活:本质上是一个业务控制器,用来做网关转发逻辑会显得很牵强,后续扩展也不方便。
注意事项
- 处理转发时,要注意请求头、请求体、请求方法的完整传递,不然下游服务可能无法正确处理请求;
- 如果是转发到外部服务,推荐用
WebClient(非阻塞)而不是RestTemplate(已过时); - 要做好异常处理,比如下游服务不可用时,返回合适的错误给客户端。
内容的提问来源于stack exchange,提问作者user3679686
相关产品推荐
相关产品推荐

