coding++:拦截器拦截requestbody数据如何防止流被读取后数据丢失
1):现在开发的项目是基于SpringBoot的maven项目,拦截器的使用很多时候是必不可少的,当有需要需要你对body中的值进行校验,例如加密验签、防重复提交、内容校验等等。
2):当你开开心心的在拦截器中通过 request.getInputStream(); 获取到body中的信息后。
你会发现你在 controlle r中使用了 @RequestBody 或者 再次 request.getInputStream() 获取数据时报错了,流数据丢失了。
3):@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 org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:967)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:901)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:872)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:661)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.boot.web.filter.ApplicationContextHeaderFilter.doFilterInternal(ApplicationContextHeaderFilter.java:55)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:96)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.boot.actuate.trace.WebRequestTraceFilter.doFilterInternal(WebRequestTraceFilter.java:110)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
错误信息 仅供参考
为了解决这个问题,思路如下:
1、读取流前先把流保存一下
2、使用过滤器拦截读取,再通过chain.doFilter(wrapper, response);将保存的流丢到后面程序处理
最简单的方案就是 先读取流,然后在将流写进去就行了
1):创建 HttpHelper.java 用于读取Body
package mlq.pic.filter.requestbodyfilter; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; 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; /**
* @author MaLQ
* @Description: 用于读取Body
* @date 2020年3月19日14:38:12
*/
public class HttpHelper { private static final Logger LOGGER = LoggerFactory.getLogger(HttpHelper.class); public static String getBodyString(HttpServletRequest request) throws IOException {
LOGGER.info("开始读取requestBody数据...");
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();
}
}
}
LOGGER.info("结束读取requestBody数据...body={}", sb.toString());
return sb.toString();
}
}
2):创建 RequestReaderHttpServletRequestWrapper.java 用于 requestBody 数据流处理
package mlq.pic.filter.requestbodyfilter; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; 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; /**
* @author MaLQ
* @Description: requestBody 数据流处理
* @date 2020年3月19日14:38:12
*/
public class RequestReaderHttpServletRequestWrapper extends HttpServletRequestWrapper { private static final Logger LOGGER = LoggerFactory.getLogger(RequestReaderHttpServletRequestWrapper.class); 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) { }
};
} }
3):创建 HttpServletRequestReplacedFilter.java 过滤器
package mlq.pic.filter.requestbodyfilter; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException; /**
* @author MaLQ
* @Description: 过滤器拦截
* @date 2020年3月19日14:38:12
*/
public class HttpServletRequestReplacedFilter implements Filter { private static final Logger LOGGER = LoggerFactory.getLogger(HttpServletRequestReplacedFilter.class); @Override
public void init(FilterConfig arg0) throws ServletException {
LOGGER.info("HttpServletRequestReplacedFilter...init");
} @Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException { LOGGER.info("HttpServletRequestReplacedFilter...doFilter"); ServletRequest requestWrapper = null;
if (request instanceof HttpServletRequest) {
requestWrapper = new RequestReaderHttpServletRequestWrapper((HttpServletRequest) request);
}
//获取请求中的流如何,将取出来的字符串,再次转换成流,然后把它放入到新request对象中。
// 在chain.doFiler方法中传递新的request对象
if (requestWrapper == null) {
chain.doFilter(request, response);
} else {
chain.doFilter(requestWrapper, response);
} } @Override
public void destroy() {
LOGGER.info("HttpServletRequestReplacedFilter...destroy");
} }
4):最后我们只需要在 Application.java 启动时加上如下代码注入过滤器即可
package mlq.pic.config; import mlq.pic.filter.requestbodyfilter.HttpServletRequestReplacedFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; @Configuration
public class FilterConfigurerConfig { @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;
} }
5):获取@RequestBody 里面的数据(在HttpServletRequest里是以流的方式传输的)
/**
* @author MaLQ
* @Description: 获取@RequestBody 里面的数据(在HttpServletRequest里是以流的方式传输的)
* @date 2020年3月19日13:39:31
*/
public static Object getBodyParam(HttpServletRequest request,Class cls) {
ServletInputStream inputStream = null;
try {
inputStream = request.getInputStream();
return JSONObject.parseObject(inputStream, Charset.forName("UTF-8"), cls);
} catch (Exception e) {
LOGGER.error("获取@RequestBody数据转换异常:error={}" + e.getMessage(), e);
}
return null;
} // 案例
WxAlbumDetailVo detailVo = (WxAlbumDetailVo) RequestUtils.getBodyParam(request, WxAlbumDetailVo.class);
以上操作就可以解决 requestbody 数据获取一次丢失问题。
coding++:拦截器拦截requestbody数据如何防止流被读取后数据丢失的更多相关文章
- 解决SpringMVC拦截器中Request数据只能读取一次的问题
解决SpringMVC拦截器中Request数据只能读取一次的问题 开发项目中,经常会直接在request中取数据,如Json数据,也经常用到@RequestBody注解,也可以直接通过request ...
- spring mvc 通过拦截器记录请求数据和响应数据
spring mvc 能过拦截器记录请求数据记录有很多种方式,主要有以下三种: 1:过滤器 2:HandlerInterceptor拦截器 3:Aspect接口控制器 但是就我个人所知要记录返回的数据 ...
- Springboot前后端分离中,后端拦截器拦截后,前端没有对应的返回码可以判断
项目登录流程如下 用户进入前端登录界面,输入账号密码等,输入完成之后前端发送请求到后端(拦截器不会拦截登录请求),后端验证账号密码等成功之后生成Token并存储到数据库,数据库中包含该Token过期时 ...
- Springboot通过拦截器拦截请求信息收集到日志
1.需求 最近在工作中遇到的一个需求,将请求中的客户端类型.操作系统类型.ip.port.请求方式.URI以及请求参数值收集到日志中,网上找资料说用拦截器拦截所有请求然后收集信息,于是就开始了操作: ...
- Struts2 拦截器—拦截action
对于拦截器的基本使用这里我就懒得打字了,我这里就讲下如何用 Struts2 拦截器 拦截action.这是我个人的想法,如果有什么不对的,或者你们有什么更好的方法.请多多留言! 拦截器的默认拦截的方法 ...
- 防止SpringMVC拦截器拦截js等静态资源文件
SpringMVC提供<mvc:resources>来设置静态资源,但是增加该设置如果采用通配符的方式增加拦截器的话仍然会被拦截器拦截,可采用如下方案进行解决: 方案一.拦截器中增加针对静 ...
- springboot springmvc拦截器 拦截POST、PUT、DELETE请求参数和响应数据,并记录操作日志
1.操作日志实体类 @Document(collection = "operation_log") @Getter @Setter @ToString public class O ...
- Mybatis拦截器 mysql load data local 内存流处理
Mybatis 拦截器不做解释了,用过的基本都知道,这里用load data local主要是应对大批量数据的处理,提高性能,也支持事务回滚,且不影响其他的DML操作,当然这个操作不要涉及到当前所lo ...
- SpringBoot 拦截器 && 拦截之后返回前台自定义格式
1.加入 阿里的 json jar包 <!--json jar相关jar包--> <dependency> <groupId>com.fasterxml.jacks ...
随机推荐
- 从头解决PKIX path building failed
从头解决PKIX path building failed的问题 本篇涉及到PKIX path building failed的原因和解决办法(包括暂时解决和长效解决的方法),也包括HTTP和HTTP ...
- 学h5前端开发前必知的三大流行趋势
学h5前端开发前必知的三大流行趋势 随着互联网时代的飞速发展,各种互联网的Web应用程序层出不穷,很多人对于HTML5前端开发的过程充满了好奇,但是却没有了解到前端开发的未来发展趋势.下面,云慧学院专 ...
- MySQL数据库无完整备份删库,除了跑路还能怎么办?
1.背景 前段时间,由于运维同事的一次误操作,清空了内网核心数据库,导致了公司内部管理系统长时间不可用,大量知识库内容由于没有备份险些丢失. 结合这两天微盟的删库跑路事件,我们可以看到,数据库的备份与 ...
- 利用wps创建有目录的PDF/word
为什么要创建: 在阅读一些行业规范或者很长的文件,像是项目管理方案时,非常麻烦,定位需要重新返回目录去.--->所以我想能不能创建一个带目录的PDF,可以点击直接跳转,那就方便多了. 如何创建: ...
- 转pdf
一.转印厂pdf(书本类及折页类) 1.储存为(Ctrl+Shift+S) 2.保存类型选择 pdf 3.常规==>Adobe PDF预设==>选择印刷质量 4.选择标记和出血==&g ...
- selenium+Python 将登录模块化
公共模块化:(登录) login.py from selenium import webdriver from time import sleep class login(): def u ...
- 【03】openlayers 地图事件
绑定事件:map.on(type, listener) 取消绑定:map.un(type, listener) type:事件类型 listener:执行得函数体 事件类型: //事件类型 let t ...
- 阿里AI芯片:12nm工艺、709平方毫米大核心
含光出自<列子·汤问>篇有“上古三剑”一章,寓意含而不露,光而不耀,象征含光 800 无形却强劲的算力. 含光 800 是一款 AI 芯片,偏重推理.据介绍,1 颗含光 800 的算力相当 ...
- python基础(初识)
Python简介 python的创始人为吉多·范罗苏姆(Guido van Rossum).1989年的圣诞节期间,吉多·范罗苏姆(中文名字:龟叔)为了在阿姆斯特丹打发时间,决心开发一个新的脚本解 ...
- vue+django+webpack搭建
一.前言 最近接手了一个项目,后端是django,前端是django自带的模板,用的是jinja2,写了一段时间发现用起来特别不顺手,于是想在保持现有的模板基础上,引入vue框架,这样同事可以继续用自 ...