接口请求一切正常,但是会报错Required request body is missing的错误。
百般查询和debug后都未查到问题,最后还是在stackOverFlow中找解决方案:
https://stackoverflow.com/questions/35549040/failed-to-read-http-message-org-springframework-http-converter-httpmessagenotre
原因是:我新增了一个Filter用于打印请求和返回日志,部分代码如下:
@OverridepublicvoiddoFilter(ServletRequestrequest,ServletResponseresponse,FilterChainchain)throwsIOException,ServletException{this.preHandle(request,(HttpServletResponse)response);chain.doFilter(request,response);this.afterCompletion((HttpServletRequest)request,(HttpServletResponse)response);}privatevoidpreHandle(HttpServletRequestrequest,HttpServletResponseresponse){try{RequestWrapperrequestWrapper=newRequestWrapper((HttpServletRequest)request);log.info("requestparams:{}",JSON.toJSON(requestWrapper.getBody()));}catch(Exceptione){log.info("logerrorignore",e);}}
@Slf4jpublicclassRequestWrapperextendsHttpServletRequestWrapper{privatefinalStringbody;publicRequestWrapper(HttpServletRequestrequest){super(request);StringBuilderstringBuilder=newStringBuilder();BufferedReaderbufferedReader=null;InputStreaminputStream=null;try{inputStream=request.getInputStream();if(inputStream!=null){bufferedReader=newBufferedReader(newInputStreamReader(inputStream));char[]charBuffer=newchar[128];intbytesRead=-1;while((bytesRead=bufferedReader.read(charBuffer))>0){stringBuilder.append(charBuffer,0,bytesRead);}}else{stringBuilder.append("");}}catch(IOExceptionex){}finally{if(inputStream!=null){try{inputStream.close();}catch(IOExceptione){log.error("inputStreamcloseerror",e);}}if(bufferedReader!=null){try{bufferedReader.close();}catch(IOExceptione){log.error("bufferedReadercloseerror",e);}}}body=stringBuilder.toString();}@OverridepublicServletInputStreamgetInputStream()throwsIOException{finalByteArrayInputStreambyteArrayInputStream=newByteArrayInputStream(body.getBytes());ServletInputStreamservletInputStream=newServletInputStream(){@OverridepublicbooleanisFinished(){returnfalse;}@OverridepublicbooleanisReady(){returnfalse;}@OverridepublicvoidsetReadListener(ReadListenerreadListener){}@Overridepublicintread()throwsIOException{returnbyteArrayInputStream.read();}};returnservletInputStream;}@OverridepublicBufferedReadergetReader()throwsIOException{returnnewBufferedReader(newInputStreamReader(this.getInputStream()));}publicStringgetBody(){returnthis.body;}}
开始总是找不到原因,最后是上述链接里的一番话,才让我恍然大悟,找到根源,见下图:
inputstream只可以读取一次!看到这句话,基本问题就迎刃而解了。最简单的方式就是在filter里不要读取inputstream,当然如果你想读取的话,可以采用下面的方式,思路也是上图中说的:decorate the httpServletRequest。代码如下:
@OverridepublicvoiddoFilter(ServletRequestrequest,ServletResponseresponse,FilterChainchain)throwsIOException,ServletException{RequestWrapperrequestWrapper=newRequestWrapper((HttpServletRequest)request);this.preHandle(requestWrapper,(HttpServletResponse)response);//此处传封装后的HttpServletRequestchain.doFilter(requestWrapper,response);this.afterCompletion((HttpServletRequest)request,(HttpServletResponse)response);}
RequestWrapper不用改,封装HttpServletRequest之后,doFilter也传入封装的HttpServletRequest即可。
总结
一. 为什么inputStream只可以读取一次
InputStream read方法内部会记录position,用于记录当前流读取到的位置,若已读完,read方法会返回-1,(经常和inputStream打交道的,应该都会while(read() != -1) ,用这种方式读取inputStream)。若读取完再次读取的话可以调用inputStream.reset方法,此方法的前提是此方法markSupported返回true。HttpServletRequest使用的是ServletInputStream,看源码可知ServletInputStream没有实现reset和markSupported方法,那么ServletInputStream无法reset之后再次读取,所以inputStream只可以读取一次! (我要是对基础有深刻的了解,就不会查这么久了。)
publicsynchronizedvoidreset()throwsIOException{thrownewIOException("mark/resetnotsupported");}/***Testsifthisinputstreamsupportsthe<code>mark</code>and*<code>reset</code>methods.Whetherornot<code>mark</code>and*<code>reset</code>aresupportedisaninvariantpropertyofa*particularinputstreaminstance.The<code>markSupported</code>method*of<code>InputStream</code>returns<code>false</code>.**@return<code>true</code>ifthisstreaminstancesupportsthemark*andresetmethods;<code>false</code>otherwise.*@seejava.io.InputStream#mark(int)*@seejava.io.InputStream#reset()*/publicbooleanmarkSupported(){returnfalse;}