Springboot如何设置过滤器及重复读取request里的body

网友投稿 789 2022-08-20


Springboot如何设置过滤器及重复读取request里的body

目录HttpServletRequest的输入流只能读取一次的原因重复读取body中数据的方法springboot的过滤器上面的getBody的代码

需求:

request的content-type为applciation/json,进入controller之前需要把body中的参数取出来做一次处理,然后和hearder中的另一个参数做对比。

思路:

加一个过滤器,在过滤器中取出参数做处理,然后比较

注意:

body里的数据用流来读取,只能读取一次

HttpServletRequest的输入流只能读取一次的原因

我们先来看看为什么HttpServletRequest的输入流只能读一次,当我们调用getInputStream()方法获取输入流时得到的是一个InputStream对象,而实际类型是ServletInputStream,它继承于InputStream。

InputStream的read()方法内部有一个postion,标志当前流被读取到的位置,每读取一次,该标志就会移动一次,如果读到最后,read()会返回-1,表示已经读取完了。如果想要重新读取则需要调用reset()方法,position就会移动到上次调用mark的位置,mark默认是0,所以就能从头再读了。调用reset()方法的前提是已经重写了reset()方法,当然能否reset也是有条件的,它取决于markSupported()方法是否返回true。

InputStream默认不实现reset(),并且markSupported()默认也是返回false,这一点查看其源码便知:

我们再来看看ServletInputStream,可以看到该类没有重写mark(),reset()以及markSupported()方法:

综上,InputStream默认不实现reset的相关方法,而ServletInputStream也没有重写reset的相关方法,这样就无法重复读取流,这就是我们从request对象中获取的输入流就只能读取一次的原因。

重复读取body中数据的方法

这个自定义的requestWrapper继承了HttpServletRequestWrapper ,HttpServletRequestWrapper 是一个ServletRequest的包装类同时也是ServletRequest的实现类。

在这个自定义的requestWrapper里,用一个String做缓存,在构造方法里先把request的body中的数据缓存起来,然后重写了getInputStream,返回这个缓存的body,而不是从流中读取。

这样,在需要多次读取body的地方,只需要在过滤器中把原来的request换成这个自定义的request,然后把这个自定义的带缓存功能的request传到后续的过滤器链中。

public class BodyReaderRequestWrapper extends HttpServletRequestWrapper {

private final String body;

/**

*

* @param request

*/

public BodyReaderRequestWrapper(HttpServletRequest request) throws IOException{

super(request);

StringBuilder sb = new StringBuilder();

InputStream ins = request.getInputStream();

BufferedReader isr = null;

try{

if(ins != null){

isr = new BufferedReader(new InputStreamReader(ins));

char[] charBuffer = new char[128];

int readCount = 0;

while((readCount = isr.read(charBuffer)) != -1){

sb.append(charBuffer,0,readCount);

}

}else{

sb.append("");

}

}catch (IOException e){

throw e;

}finally {

if(isr != null) {

isr.close();

}

}

sb.toString();

body = sb.toString();

}

@Override

public BufferedReader getReader() throws IOException {

http://return new BufferedReader(new InputStreamReader(this.getInputStream()));

}

@Override

public ServletInputStream getInputStream() throws IOException {

final ByteArrayInputStream byteArrayIns = new ByteArrayInputStream(body.getBytes());

ServletInputStream servletIns = 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 byteArrayIns.read();

}

};

return servletIns;

}

}

springboot的过滤器

2个注解:

@WebFilter(过滤器上)@ServletComponentScan (加在启动类上,支持servlet components扫描(为了webfilter))

@Order(999) // 序号越低,优先级越高

// 加上WebFilter即可成为过滤器

@WebFilter(filterName="myFilter", urlPatterns="/api/workorder/service/selfAppeal")

public class ExternalFilter implements Filter {

private final static Logger logger = LoggerFactory.getLogger(ExternalFilter.class);

@Override

public void init(FilterConfig filterConfig) throws ServletException {

logger.info("filter init");

}

@Override

public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)

throws IOException, ServletException {

ResponseObject object = new ResponseObject();

HttpServletRequest reqhttp:// = (HttpServletRequest)servletRequest;

HttpServletResponse res = (HttpServletResponse)servletResponse;

// 一个request的包装类,初始化时缓存了body,重写了getInputStream返回缓存的body,实现重复读取body

BodyReaderRequestWrapper requestWrapper = new BodyReaderRequestWrapper(req);

String requestURI = requestWrapper.getRequestURI();

System.out.println("--------------------->过滤器:请求qOffVedT地址" + requestURI);

String md5 = requestWrapper.getHeader("md5") ;

if (md5 == null || !md5.toLowerCase().equals(MD5.md5(ReqGetBody.getBody(requestWrapper)).toLowerCase())) {

object.setStatus(501);

object.setStatusText("数据md5校验失败");

render(object, res);

return;

}

// 这里传递下去的就是自定义的request了,所以后续的Controller才能重复读取到body里的参数

filterChain.doFilter(requestWrapper, res);

}

@Override

public void destroy() {

}

/**

* @Title: render

* @Description: 发送Response

* @param object

* @param response void

* @author MasterYi

* @date 2020年1月15日上午10:48:45

*/

private void render(ResponseObject object, HttpServletResponse response) {

response.setContentType("application/json;charset=UTF-8");

try {

response.setStatus(200);

response.getWriter().write(JSONObject.toJSON(object).toString());

} catch (IOException e) {

logger.errorhttp://("ExternalFilter写入response异常");

}

}

}

上面的getBody的代码

从body中取值的具体操作

public class ReqGetBody {

static public String getBody(HttpServletRequest request) {

try {

ServletInputStream in = request.getInputStream();

String body;

body = StreamUtils.copyToString(in, Charset.forName("UTF-8"));

return body;

} catch (IOException e) {

e.printStackTrace();

return "";

}

}

}


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:SpringBoot缓存Ehcache的使用详解
下一篇:java并发数据包Exchanger线程间的数据交换器
相关文章

 发表评论

暂时没有评论,来抢沙发吧~