首页 > 解决方案 > 如何访问 Spring Boot 默认错误响应的 HTTP 响应内容?

问题描述

我有一个 Spring Boot 应用程序,它正在捕获对应用程序发出的 REST 请求的 HTTP 响应内容。我这样做是为了记录和将来分析进入系统的请求。

目前,我将其实现为过滤器 bean (per OncePerRequestFilter),使用 aContentCachingResponseWrapper来捕获写入输出流的内容:

@Component
public class ResponseBodyLoggingFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(
            HttpServletRequest request, HttpServletResponse response,
            FilterChain filterChain) throws ServletException, IOException {
        ContentCachingResponseWrapper cachingResponse =
                new ContentCachingResponseWrapper(response);

        try {
            filterChain.doFilter(request, cachingResponse);
        } finally {
            byte[] responseBytes = cachingResponse.getContentInputStream().readAllBytes();
            System.out.println("Response: \"" + new String(responseBytes) + "\"");

            cachingResponse.copyBodyToResponse();
        }
    }
}

这适用于对应用程序的大多数请求。但是,它没有捕获的一件事是默认的 Spring Boot 错误响应。它不是捕获响应的内容,而是返回空字符串。

build.gradle

plugins {
    id 'org.springframework.boot' version '2.5.3'
    id 'io.spring.dependency-management' version '1.0.11.RELEASE'
    id 'java'
}

sourceCompatibility = '11'

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
}

控制器:

@RestController
@RequestMapping("/test")
public class TestController {
    @GetMapping("/404")
    public void throw404() {
        throw new ResponseStatusException(BAD_REQUEST);
    }
}

HTTP 响应:

{
  "timestamp": "2021-08-03T18:30:18.934+00:00",
  "status": 400,
  "error": "Bad Request",
  "path": "/test/404"
}

系统输出:

Response: ""

我已经确认,如果我从嵌入式 Tomcat 的 Spring Boot 默认切换到嵌入式 Jetty(使用spring-boot-starter-jetty和排除spring-boot-starter-tomcat),仍然会出现此问题。

如何在我的应用程序中捕获 Spring Boot 错误响应输出?请注意,如果另一个解决方案解决了问题,我不需要它作为过滤器。

标签: javaspringspring-bootspring-mvcservlet-filters

解决方案


我还没有确定一个好方法来实现在过滤器中获取 Spring Boot 错误响应主体的既定目标,但是经过一些调试和深入了解 Spring 内部,我相信我可能已经确定它为什么不起作用,至少。

它看起来像是BasicErrorController.error(HttpServletRequest request)框架的一部分,负责返回要呈现的错误对象。

但是,观察调用此控制器方法的位置,它看起来好像是在Servlet.service()实际过滤发生之后的调用期间发生的。每人:tomcat-embed-core_ApplicationFilterChain

private void internalDoFilter(ServletRequest request,
                              ServletResponse response)
    throws IOException, ServletException {

    // Call the next filter if there is one
    // [...]
    Filter filter = filterConfig.getFilter();
    // [...]
    filter.doFilter(request, response, this);
    // [...]

    // We fell off the end of the chain -- call the servlet instance
    // [...]
    servlet.service(request, response);
    // [...]
}

根据上面的代码,ResponseBodyLoggingFilter过滤器在 中被调用filter.doFilter(request, response, this),但BasicErrorController.error(...)直到之后才被调用servlet.service(request, response)


推荐阅读