Spring常见问题解决 - Required request body is missing
Spring常见问题解决 - Required request body is missing
- 前言
- 一. 案例复现
- 二. 原理分析
- 三. 问题解决
- 3.1 自定义适配器代替过滤器
- 3.2 包装流并返回
前言
可以看下Spring常见问题解决 - @EnableWebMvc 导致自定义序列化器失效。
一. 案例复现
可以添加一个pom
依赖:
<dependency><groupId>org.apache.commons</groupId><artifactId>commons-io</artifactId><version>1.3.2</version>
</dependency>
1.我们自定义一个过滤器MyFilter
:
import org.apache.commons.io.IOUtils;
import org.springframework.stereotype.Component;import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;/*** @author Zong0915* @date 2022/8/31 下午7:36*/
@Component
@WebFilter(urlPatterns = "/*", filterName = "myFilter")
public class MyFilter implements Filter {@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {String requestBody = IOUtils.toString(request.getInputStream(), "utf-8");System.out.println("print request body in filter:" + requestBody);chain.doFilter(request, response);}
}
2.Controller
类:
@RestController
public class MyController {@PostMapping("/hello")public User hello(@RequestBody User user){return user;}
}
3.访问对应的接口:
控制台输出如下:
这里有句话太长了,我再贴一个:
二. 原理分析
在前面的文章我讲到过关于转换器的一些问题,并且多次用一段代码来验证当前请求用的是什么转换器,代码如下AbstractMessageConverterMethodArgumentResolver.readWithMessageConverters
:
@Nullable
protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter,Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {// ...Object body = NO_VALUE;EmptyBodyCheckingHttpInputMessage message;try {message = new EmptyBodyCheckingHttpInputMessage(inputMessage);for (HttpMessageConverter<?> converter : this.messageConverters) {Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass();GenericHttpMessageConverter<?> genericConverter =(converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null);if (genericConverter != null ? genericConverter.canRead(targetType, contextClass, contentType) :(targetClass != null && converter.canRead(targetClass, contentType))) {if (message.hasBody()) {// ..对结果的转换解析}else {//处理没有 body 情况,默认返回 nullbody = getAdvice().handleEmptyBody(null, message, parameter, targetType, converterType);}break;}}}// ..return body;
}
我们得知,message
使用EmptyBodyCheckingHttpInputMessage
类型来进行包装,我们看下这个类的构造函数:
public EmptyBodyCheckingHttpInputMessage(HttpInputMessage inputMessage) throws IOException {this.headers = inputMessage.getHeaders();InputStream inputStream = inputMessage.getBody();if (inputStream.markSupported()) {inputStream.mark(1);this.body = (inputStream.read() != -1 ? inputStream : null);inputStream.reset();}else {PushbackInputStream pushbackInputStream = new PushbackInputStream(inputStream);// 当前流是否被读取过,如果被读取过就是-1,此时this.body就赋值为nullint b = pushbackInputStream.read();if (b == -1) {this.body = null;}else {this.body = pushbackInputStream;pushbackInputStream.unread(b);}}
}
而我们在过滤器定义了这段代码:
String requestBody = IOUtils.toString(request.getInputStream(), "utf-8");
正式因为这个流被读取过了,导致在后续对请求体进行解析的时候int b = pushbackInputStream.read();
发现该流的内容已经被读取完毕了,所以请求体是空。所以报出了这样的错误:
Required request body is missing
注意:
InputStream.read
方法内部会记录position
,用于记录当前流读取到的位置。若已读完,read
方法会返回-1
。因此不能重复读取。
那么我们如何解决这个问题?我们可以继续看下解析请求体的代码,有这么一段代码:
body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType,(Class<? extends HttpMessageConverter<?>>) converter.getClass(),inputMessage, outputMessage);
当一个 Body
被解析出来后,会调用 getAdvice()
来获取 RequestResponseBodyAdviceChain
;然后在这个 Chain
中,寻找合适的 Advice
并执行(即适配器)。做一些包装处理。那么我们可以基于这个特性去解决这个问题。
三. 问题解决
3.1 自定义适配器代替过滤器
方式一:自定义一个适配器MyRequestBodyAdviceAdapter
来代替我们的过滤器工作。目的就是希望读取一下请求体。
@ControllerAdvice
public class MyRequestBodyAdviceAdapter extends RequestBodyAdviceAdapter {@Overridepublic boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {return true;}@Overridepublic Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {System.out.println("MyRequestBodyAdviceAdapter-afterBodyRead: body: " + body);return super.afterBodyRead(body, inputMessage, parameter, targetType, converterType);}
}
不过这种方式有一点需要注意的是:
- 这里拿到的
body
是Object
类型的Java对象,不再是InputStream
流。 - 因此可以避免请求体以
InputStream
的形式被读取两次。导致Required request body is missing
的异常。
结果如下:
3.2 包装流并返回
方式二:我们依旧使用过滤器,依旧读取一遍InputStream
流。但是我们对齐进行包装,然后再返回。
我们自定义一个包装流对象BodyReaderWrapper
:
public class BodyReaderWrapper extends HttpServletRequestWrapper {//用于将流保存下来private byte[] requestBody;public BodyReaderWrapper(HttpServletRequest request) throws IOException {super(request);requestBody = StreamUtils.copyToByteArray(request.getInputStream());String requestBodyStr = IOUtils.toString(requestBody, "utf-8");System.out.println("print request body in filter:" + requestBodyStr);}@Overridepublic ServletInputStream getInputStream() throws IOException {final ByteArrayInputStream bais = new ByteArrayInputStream(requestBody);return new ServletInputStream() {@Overridepublic int read() throws IOException {return bais.read();}@Overridepublic boolean isFinished() {return false;}@Overridepublic boolean isReady() {return false;}@Overridepublic void setReadListener(ReadListener readListener) {}};}@Overridepublic BufferedReader getReader() throws IOException {return new BufferedReader(new InputStreamReader(getInputStream()));}
}
注意:
- 因为我们定义了一个属性,用来保存流对象。
- 因此
getInputStream()
需要重写,读取包装类中存储的流对象。 - 那么随之,
getReader()
也需要重写。
过滤器做出更改:
@Component
@WebFilter(urlPatterns = "/*", filterName = "myFilter")
public class MyFilter implements Filter {@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {// 将流对象包装一下,然后返回BodyReaderWrapper bodyReaderWrapper = new BodyReaderWrapper((HttpServletRequest) request);chain.doFilter(bodyReaderWrapper, response);}
}
结果如下:
Spring常见问题解决 - Required request body is missing相关推荐
- Java中Required request body is missing问题解决
报Required request body is missing这个问题的原因是请求类型,引入的参数注解不对应 解决 检查Controller 接口接收的参数注解 post请求使用注解@Reques ...
- 解决SpringMvc框架中提示的“Required request body is missing”异常。
之前没搞过Web后台,现在刚接手后台项目就遇到了这种异常问题,做次记录,避免更多的人跳坑. 瞎搞了两天才找到问题原因也是没谁了. 出现该"Required request body is m ...
- Spring常见问题解决 - AOP调用被拦截类的属性报NPE
Spring常见问题解决 - AOP调用被拦截类的属性报NPE 一. 案例复现 二. 被拦截类的属性为何是null? 2.1 原理分析 2.2 解决 2.2.1 为何加一个 get 方法就可以避免NP ...
- 报错,Exception: Required request body is missing: public org.springframework.ui.ModelMap cn.yihuazt.co
问题: 2021-04-29 15:06:16.795 ERROR 30460 --- [io-12000-exec-1] c.y.w.c.GlobalControllerExceptionHandl ...
- 异常Required request body is missing。
前几天在调用接口时,请求的参数明明都已经填上去了,但就是报错,Required request body is missing.两种方法 上面报异常 (1)把**@RequestBody **去掉就好 ...
- Required request body is missing错误
错误提示如下: org.springframework.http.converter.HttpMessageNotReadableException: Required request body is ...
- 解决:Required request body is missing
报错信息: WARN DefaultHandlerExceptionResolver:384 - Failed to read HTTP message: org.s ...
- 关于加入@RequestBody后请求报错:Required request body is missing:
关于加入@RequestBody后请求报错: Required request body is missing: 这个错误是由于Controller中加入了@RequestBody后却收不到指定请求体 ...
- 返回code400,报错Required request body is missing
做登录的时候,使用postman可以获取返回值,但是使用Retrofit做的时候就报错,Debug一下,发现返回code为400,message为Required request body is mi ...
- Required request body is missing
美好的一天,从解决BUG开始! org.springframework.http.converter.HttpMessageNotReadableException: Required request ...
最新文章
- [deviceone开发]-do_Dialog的基本使用示例
- Go赋值使用:类型{} 定位使用.
- com.esri.android,解决ArcGIS Android Could not find class 'com.esri.android.map.MapView'问题
- 线程间协作的两种方式:wait、notify、notifyAll和Condition
- php-5.2.6安装,php5.2.6安装openssl.o扩展,make时报错?
- 【python】内建异常类的层次
- oracle查看表空间和物理文件大小
- 报表人的福音!25个实用报表模板合集,适用多个业务场景
- jupyetr notebook添加anaconda虚拟环境内核(tensorflow+pytorch)
- eclipse 背景颜色
- 疫情下,嵌入式er该怎么进行职业规划,难点在哪?
- Python实现离线字典+听写单词(一):获取离线字典
- IEC 60601-2-33:2022 《医疗诊断用磁共振设备基本安全和基本性能的特殊要求》。
- SCCM 2016安装部署
- 勒索病毒威胁的解决方案
- 两条命令彻底修复动态链接库
- spin_lock到spin_lock_irqsave的使用
- python的argc与argv
- java基础国庆作业_国庆JAVA作业
- Python 怎么利用Python绘制二元高次隐函数的函数图像及其极值点——以某双核论文模型方程为例
热门文章
- Acm - 隔壁老王买酒问题
- 详解 python 的 切片
- 【金猿产品展】EasyTwin——国产自研数字孪生融合渲染引擎
- 线程main java中的异常怎么解决_线程“ main”中的异常java.lang.NoClassDefFoundError:...
- 智能系统概论——初识百度AI平台
- 胡昊—第8次作业--继承
- JavaFX: Alert 弹窗
- 腾讯让企业微信连接微信,这是针对钉钉的精准打击吗?
- Java中将List分组到Map中算法(可用于android联系人拼音分组)
- 2022-2027年中国喷涂机器人行业市场调研及未来发展趋势预测报告