SpringBoot之HandlerInterceptor-IO
2019-09-16 本文已影响0人
花神子
SpringBoot之HandlerInterceptor-IOE Stream close
简要说明下SpringBoot的使用HandlerInterceptor 通过request.getInputStream()获取数据报Stream closed异常分析解决。
代码实例:
@Component
public class AmsInterceptor implements HandlerInterceptor {
private static final Logger LOGGER = LoggerFactory.getLogger(AmsInterceptor.class);
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HandlerMethod method = (HandlerMethod) handler;
String cn = method.getBean().getClass().getName();
String mn = method.getMethod().getName();
String bodyString = getBodyString(request);
LOGGER.info("preHandle : [{}]#[{}]#[{}]", cn, mn, bodyString);
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
LOGGER.info("postHandle : [{}]", request.getRequestURI());
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
LOGGER.info("afterCompletion : [{}]", request.getRequestURI());
}
}
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();
}
- String bodyString = getBodyString(request); 我们在拦截器中通过request.getInputStream();获取到body中的信息后,之后在controller中使用了@RequestBody注解获取参数报如下错误:
I/O error while reading input message; nested exception is java.io.IOException: Stream closed
org.springframework.http.converter.HttpMessageNotReadableException: I/O error while reading input message; nested exception is java.io.IOException: Stream closed
at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver.readWithMessageConverters(AbstractMessageConverterMethodArgumentResolver.java:229)
at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.readWithMessageConverters(RequestResponseBodyMethodProcessor.java:150)
at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.resolveArgument(RequestResponseBodyMethodProcessor.java:128)
at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:121)
at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:158)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:128)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:97)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738)
at
...
出现原因:
spring boot项目,RequestBody里数据,只能通过流的方式获取,而在aop里获取了,在Controller里使用@RequestBody注解再获取就报
I/O error while reading input message; nested exception is java.io.IOException: Stream closed
这个流只能用一次,用过之后,就不能再取数据了。
解决方案
先读取流,然后在将流写进去,下次就可以再读取流了。
实现
1. 重写 HttpServletRequestWrapper
public class ReHttpServletRequestWrapper extends HttpServletRequestWrapper {
private final byte[] bodyBuf;
public ReHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
bodyBuf = HttpHelper.getBodyString(request).getBytes(Charset.forName("UTF-8"));
}
@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream stream = new ByteArrayInputStream(bodyBuf);
return new ServletInputStream() {
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
@Override
public int read() throws IOException {
return stream.read();
}
};
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream()));
}
}
2. 将重写 HttpServletRequestWrapper通过过滤器重新写入
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
String uri = request.getRequestURI();
long start = System.currentTimeMillis();
LOGGER.info("请求地址 : [{}]", uri);
ServletRequest requestWrapper;
if (servletRequest instanceof HttpServletRequest) {
requestWrapper = new ReHttpServletRequestWrapper((HttpServletRequest) servletRequest);
if (requestWrapper != null) {
servletRequest = requestWrapper;
}
}
filterChain.doFilter(servletRequest, servletResponse);
long end = System.currentTimeMillis();
LOGGER.info("请求地址 : [{}], 耗时 : [{}] ms", uri, (end - start));
}
这样我们就可以在Interceptor中通过request.getInputStream();获取到body中的信息