Spring MVC如何记录所有请求(含resource not found请求)
你说得太对了!Spring MVC的Interceptor确实搞不定那些「找不到资源」的404请求——毕竟Interceptor的执行逻辑是绑定在DispatcherServlet找到对应的handler之后的,连handler都找不到,Interceptor的前置、后置方法根本没机会触发。下面给你几个经过实践验证的可行方案:
解决方案1:自定义Servlet Filter(最通用的方式)
Filter是Servlet规范里的核心组件,它的执行时机早于Spring MVC的DispatcherServlet,不管请求最终能不能找到对应的handler,都会经过Filter,所以用它来统一记录所有请求非常靠谱。
import javax.servlet.*; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.time.LocalDateTime; @WebFilter(urlPatterns = "/*") public class RequestLoggingFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest httpRequest = (HttpServletRequest) request; // 记录请求核心信息 String method = httpRequest.getMethod(); String requestURI = httpRequest.getRequestURI(); String remoteAddr = httpRequest.getRemoteAddr(); LocalDateTime timestamp = LocalDateTime.now(); System.out.printf("[%s] 请求信息:%s %s,来自IP:%s%n", timestamp, method, requestURI, remoteAddr); // 继续执行请求链,让后续组件处理请求 chain.doFilter(request, response); // 这里还能记录响应状态码,比如404、200等 int status = ((javax.servlet.http.HttpServletResponse) response).getStatus(); System.out.printf("[%s] 响应状态码:%d%n", timestamp, status); } // init和destroy方法按需实现,不需要额外逻辑的话可以留空 @Override public void init(FilterConfig filterConfig) throws ServletException {} @Override public void destroy() {} }
如果是Spring Boot项目,记得在启动类上加上@ServletComponentScan注解,让Spring能扫描到这个自定义Filter。
解决方案2:捕获NoHandlerFoundException(Spring MVC通用)
默认情况下,DispatcherServlet遇到找不到handler的请求时,不会抛出异常,而是直接返回404页面。我们需要先开启「找不到handler就抛异常」的配置,再通过全局异常处理器捕获并记录请求:
配置DispatcherServlet:
- 若用XML配置(
web.xml):<servlet> <servlet-name>dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>throwExceptionIfNoHandlerFound</param-name> <param-value>true</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> - 若用Java配置(Spring Boot):
import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.DispatcherServlet; @Configuration public class WebConfig { @Bean public ServletRegistrationBean<DispatcherServlet> dispatcherServletRegistration(DispatcherServlet dispatcherServlet) { ServletRegistrationBean<DispatcherServlet> registration = new ServletRegistrationBean<>(dispatcherServlet); registration.addInitParameter("throwExceptionIfNoHandlerFound", "true"); return registration; } }
- 若用XML配置(
自定义全局异常处理器捕获
NoHandlerFoundException:import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.servlet.NoHandlerFoundException; import javax.servlet.http.HttpServletRequest; import java.time.LocalDateTime; @ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(NoHandlerFoundException.class) public String handleNoHandlerFound(NoHandlerFoundException ex, HttpServletRequest request) { // 记录404请求的详细信息 String method = request.getMethod(); String requestURI = request.getRequestURI(); String remoteAddr = request.getRemoteAddr(); LocalDateTime timestamp = LocalDateTime.now(); System.out.printf("[%s] 404 Not Found:%s %s,来自IP:%s%n", timestamp, method, requestURI, remoteAddr); // 返回自定义404页面或JSON响应,根据你的业务需求调整 return "error/404"; } }
解决方案3:自定义ErrorController(Spring Boot专属)
Spring Boot默认提供了BasicErrorController来处理所有错误请求,我们可以自定义一个ErrorController来接管错误处理逻辑,同时记录请求信息:
import org.springframework.boot.web.servlet.error.ErrorController; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletRequest; import java.time.LocalDateTime; @RestController @RequestMapping("/error") public class CustomErrorController implements ErrorController { @GetMapping public String handleError(HttpServletRequest request) { // 从请求属性中获取错误相关信息 String method = request.getMethod(); String requestURI = (String) request.getAttribute("javax.servlet.error.request_uri"); String remoteAddr = request.getRemoteAddr(); Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code"); LocalDateTime timestamp = LocalDateTime.now(); System.out.printf("[%s] 错误状态:%d,请求信息:%s %s,来自IP:%s%n", timestamp, statusCode, method, requestURI, remoteAddr); // 返回自定义错误响应,支持页面或JSON格式 return String.format("请求出错啦:状态码 %d,路径 %s", statusCode, requestURI); } }
内容的提问来源于stack exchange,提问作者Anurag Sharma
相关产品推荐
相关产品推荐

