You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot中ResponseBodyAdvice的beforeBodyWrite无法获取响应状态与头信息

问题原因及解决方案

为什么原代码无法获取响应状态码和头?

ResponseBodyAdvice.beforeBodyWrite方法的执行时机是HTTP消息转换器写入响应体之前,此时Spring MVC尚未将最终的响应状态码、响应头写入到ServletResponse中(这些元数据通常在后续请求处理阶段,比如异常处理、响应提交前才会被设置),所以你强转后获取到的是ServletResponse的初始状态,自然无法拿到实际的响应信息。

解决方案

方案1:使用HandlerInterceptor的afterCompletion方法

该方法在整个请求处理完成(响应已完全准备好,即将发送给客户端)后执行,此时可以可靠获取所有响应元数据。

第一步:实现HandlerInterceptor

@Component
@Slf4j
public class ResponseLogInterceptor implements HandlerInterceptor {

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        // 获取响应状态码
        int statusCode = response.getStatus();
        // 整理响应头为Map格式,方便日志输出
        Map<String, String> responseHeaders = new HashMap<>();
        response.getHeaderNames().forEach(headerName -> 
            responseHeaders.put(headerName, response.getHeader(headerName))
        );

        log.info("response code is {}", statusCode);
        log.info("response headers are {}", responseHeaders);
    }
}

第二步:注册拦截器

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    private final ResponseLogInterceptor responseLogInterceptor;

    public WebMvcConfig(ResponseLogInterceptor responseLogInterceptor) {
        this.responseLogInterceptor = responseLogInterceptor;
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(responseLogInterceptor)
                .addPathPatterns("/**"); // 匹配所有需要日志的请求路径
    }
}

方案2:同时记录响应体、状态码和头(推荐)

如果需要同时记录响应体,可结合ContentCachingResponseWrapper包装响应,缓存响应内容后在afterCompletion中统一获取所有信息:

@Component
@Slf4j
public class CompleteResponseLogInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 用ContentCachingResponseWrapper包装原始响应,缓存响应内容
        if (!(response instanceof ContentCachingResponseWrapper)) {
            response = new ContentCachingResponseWrapper(response);
        }
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        ContentCachingResponseWrapper cachedResponse = (ContentCachingResponseWrapper) response;

        // 获取响应状态码
        int statusCode = cachedResponse.getStatus();
        // 整理响应头
        Map<String, String> responseHeaders = new HashMap<>();
        cachedResponse.getHeaderNames().forEach(headerName -> 
            responseHeaders.put(headerName, cachedResponse.getHeader(headerName))
        );
        // 获取响应体(注意编码格式,避免乱码)
        String responseBody = new String(cachedResponse.getContentAsByteArray(), cachedResponse.getCharacterEncoding());

        log.info("response code is {}", statusCode);
        log.info("response headers are {}", responseHeaders);
        log.info("response body is {}", responseBody);

        // 必须将缓存的响应内容写出到原始响应,否则客户端无法收到响应
        cachedResponse.copyBodyToResponse();
    }
}

同样需要在WebMvcConfig中注册这个拦截器。

内容的提问来源于stack exchange,提问作者Albin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 15:39:21