现在开发的项目是基于SpringBoot的maven项目,拦截器的使用很多时候是必不可少的,当有需要需要你对body中的值进行校验,例如加密验签、防重复提交、内容校验等等。
1.问题
当你开开心心的在拦截器中通过();获取到body中的信息后,你会发现你在controller中使用了@RequestBody注解获取参数报如下错误
I/O error while reading input message; nested exception is : Stream closed
: I/O error while reading input message; nested exception is : Stream closed
at (:229)
at (:150)
at (:128)
at (:121)
at (:158)
at (:128)
at (:97)
at (:827)
at (:738)
at (:85)
at (:967)
at (:901)
at (:970)
at (:872)
at (:661)
at (:846)
at (:742)
at (:231)
at (:166)
at (:55)
at (:107)
at (:193)
at (:166)
at (:96)
at (:107)
at (:193)
at (:166)
at (:107)
at (:193)
at (:166)
at (:110)
at (:107)
at (:193)
at (:166)
2. 解决思路
IO流关闭只能读取一次,接下来我们就开始解决这个BUG,有两种大的方向:
这个需求咱不做了,特么有本事就把东西都放在请求头里面传过来;
解决流只能读取一次的问题,让它可以被多次重复读取;
考虑到生活还要继续,妻儿老小在家等着我回去告诉他们晚上吃冷面还是鸡,要这需求不给搞定那也只能去打包收拾东西滚蛋,回家路上顺便卖点血换钱了,我选择了第二种。
3.问题解决
最简单的方案就是 先读取流,然后在将流写进去就行了。
3.1 引入包
<dependency>
<groupId></groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>8.5.15</version>
</dependency>
3.2 新建HttpHelper (用于读取Body)
package ;
import ;
import ;
import ;
import ;
import ;
import ;
public class HttpHelper {
public static String getBodyString(HttpServletRequest request) throws IOException {
StringBuilder sb = new StringBuilder();
InputStream inputStream = null;
BufferedReader reader = null;
try {
inputStream = ();
reader = new BufferedReader(new InputStreamReader(inputStream, ("UTF-8")));
String line = "";
while ((line = ()) != null) {
(line);
}
} catch (IOException e) {
();
} finally {
if (inputStream != null) {
try {
();
} catch (IOException e) {
();
}
}
if (reader != null) {
try {
();
} catch (IOException e) {
();
}
}
}
return ();
}
}
3.3 新建RequestReaderHttpServletRequestWrapper(防止流丢失)
package ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
public class RequestReaderHttpServletRequestWrapper extends HttpServletRequestWrapper{
private final byte[] body;
public RequestReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
body = (request).getBytes(("UTF-8"));
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream()));
}
@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream bais = new ByteArrayInputStream(body);
return new ServletInputStream() {
@Override
public int read() throws IOException {
return ();
}
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
};
}
}
3.4 新建HttpServletRequestReplacedFilter(过滤器)
package ;
import .*;
import ;
import ;
public class HttpServletRequestReplacedFilter implements Filter {
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
ServletRequest requestWrapper = null;
if(request instanceof HttpServletRequest) {
requestWrapper = new RequestReaderHttpServletRequestWrapper((HttpServletRequest) request);
}
//获取请求中的流如何,将取出来的字符串,再次转换成流,然后把它放入到新request对象中。
// 在方法中传递新的request对象
if(requestWrapper == null) {
(request, response);
} else {
(requestWrapper, response);
}
}
@Override
public void init(FilterConfig arg0) throws ServletException {
}
}
最后我们只需要在中加上如下代码注入过滤器即可
@Bean
public FilterRegistrationBean httpServletRequestReplacedRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean();
(new HttpServletRequestReplacedFilter());
("/*");
("paramName", "paramValue");
("httpServletRequestReplacedFilter");
(1);
return registration;
}
4.测试
如下代码即可在拦截其中获取body且保证了controller中依旧可以再次获取
(request);