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

Spring中如何创建读取请求体且不破坏请求的过滤器?

解决方案:读取SOAP请求体同时保证后续流程正常使用

你的核心问题是HttpServletRequest的输入流仅能读取一次,直接读取后后续SOAP处理器无法获取请求体;而默认的ContentCachingRequestWrapper缓存容量过小(默认2048字节),若SOAP请求体超过这个大小会被截断,导致解析失败返回400错误。

以下是两种可行的解决方法:

方法一:调整ContentCachingRequestWrapper缓存容量

直接使用Spring提供的ContentCachingRequestWrapper,但设置足够大的缓存容量以容纳完整SOAP请求体:

@Component
@RequiredArgsConstructor
public class AddHashFilter extends OncePerRequestFilter {

    private final RequestHash hash;
    // 根据实际业务设置缓存大小,例如10MB
    private static final int MAX_CACHE_SIZE = 10 * 1024 * 1024;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        // 创建带大缓存的请求包装类
        ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper(request, MAX_CACHE_SIZE);
        
        // 从缓存中读取完整请求体,无需直接操作输入流
        String requestBody = new String(wrappedRequest.getContentAsByteArray(), StandardCharsets.UTF_8);
        hash.hash(requestBody);
        
        // 将包装后的请求传递给后续流程
        filterChain.doFilter(wrappedRequest, response);
    }
}

说明

  • getContentAsByteArray()会直接返回缓存的完整请求体,避免了重复读取输入流的问题
  • 自定义MAX_CACHE_SIZE确保不会因请求体过大被截断

方法二:自定义可重复读取的请求包装类

如果需要更灵活的控制,可以自己实现HttpServletRequestWrapper,将请求体一次性缓存到字节数组中:

1. 实现包装类

public class RepeatableRequestWrapper extends HttpServletRequestWrapper {
    private final byte[] body;

    public RepeatableRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        // 一次性读取原始请求体并缓存
        body = StreamUtils.copyToByteArray(request.getInputStream());
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        // 返回基于缓存字节数组的输入流,支持重复读取
        return new ServletInputStream() {
            private final ByteArrayInputStream byteStream = new ByteArrayInputStream(body);

            @Override
            public int read() throws IOException {
                return byteStream.read();
            }

            @Override
            public boolean isFinished() {
                return byteStream.available() == 0;
            }

            @Override
            public boolean isReady() {
                return true;
            }

            @Override
            public void setReadListener(ReadListener listener) {
                // 同步场景下无需实现该方法
            }
        };
    }

    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(getInputStream(), StandardCharsets.UTF_8));
    }

    // 提供直接获取请求体的方法
    public String getRequestBody() {
        return new String(body, StandardCharsets.UTF_8);
    }
}

2. 在过滤器中使用

@Component
@RequiredArgsConstructor
public class AddHashFilter extends OncePerRequestFilter {

    private final RequestHash hash;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        RepeatableRequestWrapper wrappedRequest = new RepeatableRequestWrapper(request);
        
        // 获取缓存的请求体计算哈希
        hash.hash(wrappedRequest.getRequestBody());
        
        // 传递包装后的请求给后续流程
        filterChain.doFilter(wrappedRequest, response);
    }
}

额外注意事项

  • 确保你的过滤器执行顺序在SOAP相关处理器之前,可以通过@Order注解指定优先级
  • 如果SOAP请求使用非UTF-8编码,需替换代码中的StandardCharsets.UTF_8为request.getCharacterEncoding()(需确保请求头正确设置了编码)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 09:20:30