Spring Boot中读取HttpServletRequest请求体后如何转发同请求至另一端点
网关中重复读取请求体的解决方案
方法一:自定义HttpServletRequestWrapper缓存请求体(Servlet环境)
核心思路:通过包装类缓存已读取的请求体,后续调用getInputStream()/getReader()时直接返回缓存内容
- 实现步骤:
- 编写自定义请求包装类,继承
HttpServletRequestWrapper:public class CachedBodyHttpServletRequest extends HttpServletRequestWrapper { private byte[] cachedBody; public CachedBodyHttpServletRequest(HttpServletRequest request) throws IOException { super(request); // 读取原始请求体并缓存 this.cachedBody = StreamUtils.copyToByteArray(request.getInputStream()); } @Override public ServletInputStream getInputStream() throws IOException { return new CachedServletInputStream(this.cachedBody); } @Override public BufferedReader getReader() throws IOException { return new BufferedReader(new InputStreamReader(new ByteArrayInputStream(this.cachedBody))); } // 实现自定义ServletInputStream private static class CachedServletInputStream extends ServletInputStream { private final ByteArrayInputStream inputStream; public CachedServletInputStream(byte[] cachedBody) { this.inputStream = new ByteArrayInputStream(cachedBody); } @Override public int read() throws IOException { return inputStream.read(); } @Override public boolean isFinished() { return inputStream.available() == 0; } @Override public boolean isReady() { return true; } @Override public void setReadListener(ReadListener listener) { throw new UnsupportedOperationException("不支持该操作"); } } } - 编写Filter替换原始请求:
@WebFilter(filterName = "CachedBodyFilter", urlPatterns = "/*") public class CachedBodyFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest httpRequest = (HttpServletRequest) request; // 用包装类替换原始请求 CachedBodyHttpServletRequest cachedRequest = new CachedBodyHttpServletRequest(httpRequest); chain.doFilter(cachedRequest, response); } } - 启动类添加
@ServletComponentScan注解,让Filter生效。
- 编写自定义请求包装类,继承
方法二:Spring Cloud Gateway专属方案(响应式环境)
如果使用Spring Cloud Gateway,不要直接操作原生Servlet API,改用WebFlux提供的响应式API处理
- 编写全局过滤器缓存请求体:
@Component public class RequestBodyCacheFilter implements GlobalFilter, Ordered { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { return exchange.getRequest().getBody() .cache() // 缓存请求体,支持重复订阅 .flatMap(dataBuffer -> { // 读取请求体并存入数据库 byte[] bodyBytes = new byte[dataBuffer.readableByteCount()]; dataBuffer.read(bodyBytes); String requestBody = new String(bodyBytes); // 执行数据库存储逻辑 // ... // 重新构建请求,将缓存的请求体放回 ServerHttpRequest newRequest = exchange.getRequest().mutate() .body(BodyInserters.fromValue(bodyBytes)) .build(); return chain.filter(exchange.mutate().request(newRequest).build()); }); } @Override public int getOrder() { return Ordered.HIGHEST_PRECEDENCE; // 确保过滤器最先执行 } }
关键注意事项
- Servlet环境下,Filter的执行顺序必须优先于所有需要读取请求体的组件,保证请求被提前包装。
- 处理大请求体时,避免内存溢出,可改用临时文件存储缓存内容。
- Spring Cloud Gateway中禁止混用原生Servlet API,否则会破坏响应式流程,导致后续无法读取请求体。
内容的提问来源于stack exchange,提问作者Ankitha N
相关产品推荐
相关产品推荐

