一、問題由來
自己目前在做一個小程式的後臺,已經寫好了專案中的很多的介面,同時也在進行一些修改,比如新增攔截器,統一校驗一個固定的引數是否正確。
在自己新增攔截器之前,這些介面都可以正常訪問,可是在新增攔截器之後,再次訪問介面就出現異常,異常資訊為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