Spring Boot @PostMapping如何同时获取原始Body字符串与对象映射
解决方案
要在Spring中同时实现请求体到对象的自动映射,并且获取原始JSON字符串进行签名验证,核心问题是HttpServletRequest的输入流只能读取一次,直接多次读取会导致报错。可以通过Spring提供的ContentCachingRequestWrapper包装请求,缓存请求体内容,从而实现重复读取。
步骤1:编写请求体缓存过滤器
创建一个过滤器,将请求包装为可缓存请求体的ContentCachingRequestWrapper,确保后续既能让Spring自动反序列化对象,又能获取原始JSON:
import org.springframework.web.filter.OncePerRequestFilter; import org.springframework.web.util.ContentCachingRequestWrapper; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; public class CachingRequestBodyFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper(request); filterChain.doFilter(wrappedRequest, response); } }
步骤2:注册过滤器
在Spring配置类中注册该过滤器,指定需要应用的接口路径(比如你的/wc-order接口):
import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class WebConfig { @Bean public FilterRegistrationBean<CachingRequestBodyFilter> cachingRequestBodyFilter() { FilterRegistrationBean<CachingRequestBodyFilter> registrationBean = new FilterRegistrationBean<>(); registrationBean.setFilter(new CachingRequestBodyFilter()); registrationBean.addUrlPatterns("/wc-order"); return registrationBean; } }
步骤3:修改控制器方法
在控制器中注入HttpServletRequest,将其转为ContentCachingRequestWrapper后即可获取原始请求体字符串,同时保留@RequestBody Order order的自动映射:
@PostMapping(value = "/wc-order") public void getWcOrder(@RequestBody Order order, @RequestHeader Map<String, String> headers, HttpServletRequest request) { String webhookSignature = headers.get("x-wc-webhook-signature"); // 获取原始JSON请求体 ContentCachingRequestWrapper wrappedRequest = (ContentCachingRequestWrapper) request; byte[] rawBodyBytes = wrappedRequest.getContentAsByteArray(); String plainJsonBody = new String(rawBodyBytes, request.getCharacterEncoding()); String hashedBody = doHMAC(plainJsonBody); if (hashedBody.equals(webhookSignature)) { // 签名验证通过,执行业务逻辑 } }
原理说明
ContentCachingRequestWrapper会在第一次读取请求体时将内容缓存到内部字节数组中,后续无论是Spring MVC的@RequestBody反序列化,还是你手动读取,都会使用缓存的内容,避免了输入流只能读取一次的限制。
内容的提问来源于stack exchange,提问作者Fabo137
相关产品推荐
相关产品推荐

