告别Filter!Spring Boot 轻松实现 Request Body 重复读取
《Spring Boot 3实战案例锦集》PDF电子书已更新至130篇!
🎉🎉《Spring Boot实战案例合集》目前已更新213个案例,我们将持续不断的更新。文末有电子书目录。→ 现在就订阅合集
环境:Spring Boot 3.5.0
1. 简介
在 Spring Boot 中,你无法像下面这样直接多次使用 @RequestBody 注解来读取请求体内容:
@PostMappingpublic Map<String, Object> create(@RequestBody User user, @RequestBody Address address) {// ...}
Spring MVC 底层的实现机制决定了请求体(Request Body)只能被读取一次。这是因为 HTTP 请求体是一个输入流(InputStream),一旦被读取,流就会关闭,后续再次尝试读取时,流已经处于结束状态,无法再次获取数据。
要解决请求体只能读取一次的问题,通常我们会通过 Filter + 自定义 HttpServletRequestWrapper 的方式实现请求体的多次读取。详细的实现请查看下面链接:
SpringBoot接口参数如何使用多个@RequestBody?
本篇文章,我们将通过一种新的方式实现请求体的多次读取。
2.实战案例
2.1 自定义参数解析器
我们知道@RequestBody注解的参数会通过 RequestResponseBodyMethodProcessor 读取请求body内容。我们通过替换默认的实现多次读取body。
public class RepeatReadBodyMethodProcessor extends RequestResponseBodyMethodProcessor {public RepeatReadBodyMethodProcessor(List<HttpMessageConverter<?>> converters,List<Object> requestResponseBodyAdvice) {super(converters, requestResponseBodyAdvice);}@Overrideprotected ServletServerHttpRequest createInputMessage(NativeWebRequest webRequest) {HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);Assert.state(servletRequest != null, "No HttpServletRequest");return new RepeatBodyHttpRequest(servletRequest) ;}}
这里我们实现重复读取请求body的核心是自定义的RepeatBodyHttpRequest对象:
public class RepeatBodyHttpRequest extends ServletServerHttpRequest {private static final String REQ_BODY_CONTENT = "req_body_content" ;private HttpServletRequest request ;public RepeatBodyHttpRequest(HttpServletRequest request) {super(request);this.request = request ;}@Overridepublic InputStream getBody() throws IOException {byte[] body = (byte[]) this.request.getAttribute(REQ_BODY_CONTENT) ;if (body != null) {return new ByteArrayInputStream(body) ;}InputStream is = super.getBody() ;body = StreamUtils.copyToByteArray(is) ;this.request.setAttribute(REQ_BODY_CONTENT, body) ;return new ByteArrayInputStream(body) ;}}
我们将请求body缓存到当前的request对象中,这样下次再次读取body时先判断request中是否存在。
2.2 注册参数解析器
上面实现的自定义参数解析器,我们不能通过自定义 WebMvcConfigurer#addArgumentResolvers 方法注册,这种方式注册的参数解析器优先级没有系统默认的高。我们可以通过如下方式(不止这一种方式)
@Componentpublic class ConfigArgumentProcessor {private final RequestMappingHandlerAdapter requestMappingHandlerAdapter ;public ConfigArgumentProcessor(RequestMappingHandlerAdapter requestMappingHandlerAdapter) {this.requestMappingHandlerAdapter = requestMappingHandlerAdapter;}@PostConstructprivate void init() {List<HandlerMethodArgumentResolver> originResolvers = this.requestMappingHandlerAdapter.getArgumentResolvers() ;List<HandlerMethodArgumentResolver> handlers = new ArrayList<>() ;for (HandlerMethodArgumentResolver handler : originResolvers) {if (handler instanceof RequestResponseBodyMethodProcessor) {try {Field field = RequestMappingHandlerAdapter.class.getDeclaredField("messageConverters") ;field.setAccessible(true) ;List<HttpMessageConverter<?>> messageConverters = (List<HttpMessageConverter<?>>) field.get(requestMappingHandlerAdapter) ;field = RequestMappingHandlerAdapter.class.getDeclaredField("requestResponseBodyAdvice") ;field.setAccessible(true) ;List<Object> requestResponseBodyAdvice = (List<Object>) field.get(this.requestMappingHandlerAdapter) ;handlers.add(new RepeatReadBodyMethodProcessor(messageConverters, requestResponseBodyAdvice)) ;}}handlers.add(handler) ;}this.requestMappingHandlerAdapter.setArgumentResolvers(handlers) ;}}
这样我们就替换了默认的实现,而对于原有的功能没有任何的影响。
2.3 测试
@RestController@RequestMapping("/repeatbody")public class RepeatBodyController {@PostMappingpublic Map<String, Object> create(@RequestBody User user,@RequestBody Address address) {return Map.of("user", user, "address", address) ;}public static record User(String name, Integer age, String email) {}public static record Address(String province, String city, String county) {}}
运行结果

以上是本篇文章的全部内容,如对你有帮助帮忙点赞+转发+收藏
性能杀手!JPA 、MyBatis经典的N+1问题,正在拖垮你的系统
漂亮!5种验证码!Spring Boot 集成超强验证码,安全又高效!
API接口优化!Spring Boot 接口优化新思路,性能提升35%
@RequestBody已弃用!Spring Boot 一个注解实现任意JSON的读取
性能相差60倍!Spring Boot 批量插入你做对了吗?5种方式对比
Spring Boot + iTextPdf:3步实现HTML模板生成pdf
太强了!Spring Boot 全方位的请求/响应日志记录【神级开源组件】
更多推荐


所有评论(0)