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();
}
方法:
/**
* 获取 Body 参数
*
* @param request
* @return
* @throws IOException
*/public static Map<String, Object> getAllRequestParam(final HttpServletRequest request) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream()));
String str = "";
StringBuilder wholeStr = new StringBuilder();
//一行一行的读取body体里面的内容;
while ((str = reader.readLine()) != null) {
wholeStr.append(str);
}
//转化成json对象
return JSONObject.parseObject(wholeStr.toString(), Feature.OrderedField);
}
String bodyString = getBodyString(request);
我们在拦截器中通过request.getInputStream();
获取到body中的信息后,之后在controller
中使用了@RequestBody
注解获取参数报如下错误:
java.io.IOException: UT010029: Stream is closed
Controller方法:
@RequestMapping(value = {"/getOnlineService"}, method = {RequestMethod.POST}, produces = "application/json;charset=UTF-8")
public ApiReply getOnlineServiceByBusinessId(@Validated @RequestBody BusinessCateQuery query) {
logger.info("getOnlineServiceByBusinessId method param {}", query.toString());
ApiReply resp = new ApiReply();
try {
List<CustServiceRep> serviceList = businessClassService.getOnlineServiceByBusinessId(query.getBusinessId(), query.getOrganId());
resp.setModel(serviceList);
resp.setMessage("success");
} catch(Exception e) {
logger.error("根据业务分类获取在线客服接口错误:_" + e.getMessage(), e);
return new ApiReply(ApiReplyCode.FAILED);
}
return resp;
}
二、出现原因
spring boot项目,RequestBody里数据,只能通过流的方式获取,而在aop里获取了,在Controller里使用@RequestBody注解再获取就报
I/O error while reading input message; nested exception is java.io.IOException: Stream closed
这个流只能用一次,用过之后,就不能再取数据了。
因为我们在AOP里边有获取body的调用,所以,再controller中使用@RequestBody时就报错了。
三、解决方案
先读取流,然后在将流写进去,下次就可以再读取流了。
实现
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
中的信息。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。