一、問題由來
自己目前在做一個小程序的后台,已經寫好了項目中的很多的接口,同時也在進行一些修改,比如添加攔截器,統一校驗一個固定的參數是否正確。
在自己添加攔截器之前,這些接口都可以正常訪問,可是在添加攔截器之后,再次訪問接口就出現異常,異常信息為Required request body is missing
讓我有些疑惑,之前還好好的,怎么突然就訪問報錯了呢?這個錯誤簡直莫名其妙,感覺毫無道理,可是問題卻實實在在出現。
二、問題分析
對於出現的這個問題,我首先是把錯誤信息往百度里面進行搜索,出現各種各樣的答案,可是都不是我遇到的問題的答案。當查看各種答案,查看得比較
多的時候,慢慢的理解了出現這個問題的原因。一開始我理解的是,使用注解的方式不對,看了很多篇博文之后才發現問題所在,才搞清楚問題在哪里。
出現這個問題的原因是java后台項目在處理請求時,如果是使用請求對象獲取輸入流的方式來獲取請求參數,只能獲取一次請求參數;當第二次獲取參數
時就會出現問題,報標題中的錯誤。至此終於搞清楚問題的原因,request.getInputStream()只能調用一次,我已經在攔截器中調用一次,當再使用注解
@RequestBody的時候,底層也是調用getInputStream()方法,因此拋出異常。
三、解決方案
自己去百度里面查看了很多的答案,也嘗試了很多的方案,反反復復的嘗試,希望能夠解決問題,最終找到一種解決方案。解決方式為給所有的請求都
使用自定義的請求對象,在過濾器中進行處理。在攔截器中同樣也是使用自定義的請求對象,這樣就可以解決這個問題。原理就是,在自定義的請求對象
中,將獲取請求的輸入對象流用一個變量保存起來,然后在生成一個新的請求對象即可,代碼我已經粘貼在下面:
獲取請求參數的方法
/* @Description: 抽取獲取請求參數的方法
* @author: dengchao
* @date: 2021/8/18 16:51
* @param: request 請求對象
* @return: String
*/
private String getLoginToken(HttpServletRequest request) throws IOException {
HttpServletRequest requestWrapper = new BodyReaderWrapper(request);
StringBuffer sb = new StringBuffer() ;
InputStream is = requestWrapper.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String s = "" ;
while((s=br.readLine()) != null){
sb.append(s) ;
}
//獲取請求參數示例 { "loginToken": "9a1cdf7a143047c8ad5eee87dbdfd24a", "userPhone":"15215426598", "msgType":1}
String str = sb.toString();
JSONObject jsonObject = JSONObject.parseObject(str);
String loginToken = jsonObject.getString("loginToken");
log.info("loginToken--->" + loginToken);
return loginToken;
}
自定義請求類
public class BodyReaderWrapper extends HttpServletRequestWrapper{
//用於將流保存下來
private byte[] requestBody;
public BodyReaderWrapper(HttpServletRequest request) throws IOException {
super(request);
requestBody = StreamUtils.copyToByteArray(request.getInputStream());
}
@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream bais = new ByteArrayInputStream(requestBody);
return new ServletInputStream() {
@Override
public int read() throws IOException {
return bais.read();
}
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
};
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream()));
}
}
使用過濾器來處理所有的請求
@Component
@WebFilter
public class ReplaceStreamFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
BodyReaderWrapper bodyReaderWrapper = new BodyReaderWrapper((HttpServletRequest) request);
chain.doFilter(bodyReaderWrapper, response);
}
@Override
public void destroy() {
}
}
參考文章
https://blog.csdn.net/m0_37542889/article/details/82889617
https://zhuanlan.zhihu.com/p/239948869
https://www.jb51.net/article/193961.htm