现在开发的项目是基于SpringBoot的maven项目,拦截器的使用很多时候是必不可少的,当有需要需要你对body中的值进行校验,例如加密验签、防重复提交、内容校验等等。
当你开开心心的在拦截器中通过();获取到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)
IO流关闭只能读取一次,接下来我们就开始解决这个BUG,有两种大的方向:
这个需求咱不做了,特么有本事就把东西都放在请求头里面传过来;
解决流只能读取一次的问题,让它可以被多次重复读取;
考虑到生活还要继续,妻儿老小在家等着我回去告诉他们晚上吃冷面还是鸡,要这需求不给搞定那也只能去打包收拾东西滚蛋,回家路上顺便卖点血换钱了,我选择了第二种
最简单的方案就是 先读取流,然后在将流写进去就行了
引入包
<dependency>
<groupId></groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>8.5.15</version>
</dependency>
新建HttpHelper (用于读取Body)
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
public class HttpHelper {
public static String getBodyString(HttpServletRequest request) throws IOException {
StringBuilder sb = new StringBuilder();
InputStream inputStream = null;
BufferedReader reader = null;
try {
inputStream = request.getInputStream();
reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8")));
String line = "";
while ((line = reader.readLine()) != null) {
sb.append(line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return sb.toString();
}
}
新建RequestReaderHttpServletRequestWrapper(防止流丢失)
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
public class RequestReaderHttpServletRequestWrapper extends HttpServletRequestWrapper{
private final byte[] body;
public RequestReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
body = HttpHelper.getBodyString(request).getBytes(Charset.forName("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 bais.read();
}
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
};
}
}
新建HttpServletRequestReplacedFilter(过滤器)
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
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) {
chain.doFilter(request, response);
} else {
chain.doFilter(requestWrapper, response);
}
}
@Override
public void init(FilterConfig arg0) throws ServletException {
}
}
最后我们只需要在中加上如下代码注入过滤器即可
@Bean
public FilterRegistrationBean httpServletRequestReplacedRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new HttpServletRequestReplacedFilter());
registration.addUrlPatterns("/*");
registration.addInitParameter("paramName", "paramValue");
registration.setName("httpServletRequestReplacedFilter");
registration.setOrder(1);
return registration;
}
如下代码即可在拦截其中获取body且保证了controller中依旧可以再次获取
HttpHelper.getBodyString(request);
HandlerInterceptor拦截器的使用 (4)—— 防重复提交