Spring Boot中如何将POST请求体同时解析为实体和原始字符串
根因说明
Spring 不支持同时声明两个@RequestBody的本质原因是 HTTP 请求体的输入流默认只能读取一次,读完后无法再次消费,所以无法同时支持多次反序列化。针对你的需求,以下是两种成熟的实现方案:
方案1:使用Spring自带请求缓存包装类(改动最小)
用ContentCachingRequestWrapper缓存请求体字节数组,实现重复读取,不需要修改现有反序列化逻辑:
- 先注册请求缓存Filter,仅针对JSON接口生效
import org.springframework.web.util.ContentCachingRequestWrapper; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import java.io.IOException; @WebFilter(urlPatterns = "/example/*") public class CachingRequestBodyFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest httpServletRequest = (HttpServletRequest) request; if (httpServletRequest.getContentType() != null && httpServletRequest.getContentType().contains("application/json")) { chain.doFilter(new ContentCachingRequestWrapper(httpServletRequest), response); return; } chain.doFilter(request, response); } }
- 启动类添加
@ServletComponentScan注解使Filter生效,大容量请求可在配置文件调整缓存阈值:
# application.properties,根据实际请求大小调整 spring.web.content-caching.max-size=20MB
- 接口写法如下:
@RestController @RequestMapping("/example") public class ExampleController { @PostMapping("") public void example( @RequestBody ExampleRequest exampleRequest, HttpServletRequest request ) { ContentCachingRequestWrapper wrapper = (ContentCachingRequestWrapper) request; String stringBody = new String(wrapper.getContentAsByteArray(), StandardCharsets.UTF_8); // 用stringBody做请求校验 // 用exampleRequest处理业务逻辑 } }
方案2:自定义泛型包装类(复用性更强)
如果多个接口都有同类需求,可以自定义参数解析器,一次注入同时拿到原始请求体和解析后的实体:
- 定义泛型包装类
public class RequestWrapper<T> { private String rawBody; private T parsedBody; public RequestWrapper(String rawBody, T parsedBody) { this.rawBody = rawBody; this.parsedBody = parsedBody; } // getter方法省略 }
- 自定义参数解析器
import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.core.MethodParameter; import org.springframework.web.bind.support.WebDataBinderFactory; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.ModelAndViewContainer; import javax.servlet.http.HttpServletRequest; import java.nio.charset.StandardCharsets; import org.springframework.util.StreamUtils; public class RequestWrapperArgumentResolver implements HandlerMethodArgumentResolver { private final ObjectMapper objectMapper; public RequestWrapperArgumentResolver(ObjectMapper objectMapper) { this.objectMapper = objectMapper; } @Override public boolean supportsParameter(MethodParameter parameter) { return parameter.getParameterType().equals(RequestWrapper.class); } @Override public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class); String rawBody = StreamUtils.copyToString(request.getInputStream(), StandardCharsets.UTF_8); Class<?> parsedType = (Class<?>) ((ParameterizedType) parameter.getGenericParameterType()).getActualTypeArguments()[0]; Object parsedBody = objectMapper.readValue(rawBody, parsedType); return new RequestWrapper<>(rawBody, parsedBody); } }
- 注册解析器到Spring MVC配置
import org.springframework.context.annotation.Configuration; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import java.util.List; @Configuration public class WebConfig implements WebMvcConfigurer { private final ObjectMapper objectMapper; public WebConfig(ObjectMapper objectMapper) { this.objectMapper = objectMapper; } @Override public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) { resolvers.add(new RequestWrapperArgumentResolver(objectMapper)); } }
- 接口写法简化为:
@RestController @RequestMapping("/example") public class ExampleController { @PostMapping("") public void example(RequestWrapper<ExampleRequest> requestWrapper) { String stringBody = requestWrapper.getRawBody(); ExampleRequest exampleRequest = requestWrapper.getParsedBody(); // 业务逻辑 } }
内容的提问来源于stack exchange,提问作者lilBunnyRabbit
相关产品推荐
相关产品推荐

