Spring Boot自定义DispatcherServlet异常时重复日志问题求助
问题分析
当控制器抛出ResponseStatusException这类异常时,Spring Boot的默认错误处理逻辑会将请求转发到/error端点生成标准错误响应,这直接导致自定义DispatcherServlet被执行两次:
- 第一次是原始的
/api/test/error请求,此时控制器抛出异常还未生成响应体,日志中无响应体 - 第二次是转发后的
/error请求,此时请求体已被读取无法重复获取,日志中无请求体
解决方案
方案1:过滤/error端点日志
在日志方法中判断请求URI,直接跳过/error请求的日志记录:
private void log(ContentCachingRequestWrapper request, ContentCachingResponseWrapper response) { String requestUri = request.getRequestURI(); if ("/error".equals(requestUri)) { return; } // 原有日志逻辑 System.out.println("Logging..."); }
这种方式简单直接,只保留业务请求的日志记录。
方案2:改用OncePerRequestFilter实现(推荐)
自定义DispatcherServlet并非记录请求响应日志的最佳实践,Spring官方推荐使用OncePerRequestFilter,它能保证一次请求仅执行一次过滤逻辑,天然避免重复执行问题:
实现日志Filter
@Component public class RequestResponseLoggingFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper(request); ContentCachingResponseWrapper wrappedResponse = new ContentCachingResponseWrapper(response); try { filterChain.doFilter(wrappedRequest, wrappedResponse); } finally { log(wrappedRequest, wrappedResponse); wrappedResponse.copyBodyToResponse(); } } private void log(ContentCachingRequestWrapper request, ContentCachingResponseWrapper response) { // 原有日志逻辑 System.out.println("Logging..."); } }
清理原有配置
删除之前自定义的dispatcherServlet() Bean注册,无需替换默认的DispatcherServlet。
该方案优势:
- 符合Spring生态最佳实践,Filter是专门处理请求拦截、日志的组件
OncePerRequestFilter自动处理请求转发、包含等场景,确保逻辑只执行一次- 无需额外判断
/error端点,逻辑更简洁
方案3:关联原始请求与错误响应(进阶)
如果需要关联原始请求和错误响应的日志,可以通过请求属性获取原始请求信息:
private void log(ContentCachingRequestWrapper request, ContentCachingResponseWrapper response) { String originalUri = (String) request.getAttribute(RequestDispatcher.FORWARD_REQUEST_URI); if (originalUri != null) { // 记录错误响应对应的原始请求URI System.out.println("Logging error response for original request: " + originalUri); } else { // 记录正常业务请求日志 System.out.println("Logging..."); } }
这种方式可以把两次请求的日志关联起来,便于问题排查,但需要额外处理日志关联逻辑。
内容的提问来源于stack exchange,提问作者t4dohx
相关产品推荐
相关产品推荐

