您的位置:首页 > 理论基础 > 计算机网络

spring boot 学习笔记(5) 解决HttpServletRequest inputStream只能读取一次的问题

2017-09-15 21:18 841 查看
最近打算在之前的项目APP接口里面加入验证签名的功能,实现思路很简单,就是通过添加filter的方式,在filter中读取所有的请求参数,然后验证客户端传过来的SIGN值跟服务端生成的SIGN值是否一致。

目前的接口里面参数有K=V格式的,也有JSON格式的,对于前者在filter中通过HttpServletRequest.getParameterMap就可以直接获取。但是对于JSON参数,我们需要从request的inputStream中读取,当然这也不复杂,几行代码就可以搞定了。

但是当我完成filter中的代码,在进行调试的时候,发现原来controller中通过@RequestBody获取JSON参数的接口抛出“Required request body is missing”的错误。刚开始还以为参数传的有问题,查阅相关资料才明白inputStream的数据只能读取一次,从inputStream中读取过数据之后,后续再从inputStream中就不能再读取到数据了。然后就继续在网上翻资料,有的说需要自己重写HttpServletRequestWrapper,有的说通过spring自带的ContentCachingRequestWrapper就可以,我是一个比较懒的人,如果有现成好用的我才懒得自己写呢,先试试看。

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper((HttpServletRequest) request);
String body = IOUtils.toString(request.getInputStream(),request.getCharacterEncoding());
//TODO 验证签名

chain.doFilter(requestWrapper, response);
}

然而并没有什么用 WTF ... 查看ContentCachingRequestWrapper源码发现它也只是把inputStream的数据读取出来存到cachedContent里面了,后面仍然不能再次读取inputStream, 但是我看网上有几个都说这样可以,后来才发现他们是在 chain.doFilter(requestWrapper, response)之后再通过requestWrapper.getContentAsByteArray方法获取数据,或者在filter中读取了inputstream之后,在controller接口方法中使用requestWrapper.getContentAsByteArray方法再获取数据,这样就不能使用@RequestBody自动注入参数了,这显然不是我想要的。

看来还得自己动手,我们要做的就是读取了inputStream之后,再将数据写回去,具体实现如下:

package com.spbd.core.web.filter;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

import org.apache.commons.io.IOUtils;

public class ContentCachingRequestWrapper extends HttpServletRequestWrapper{

private byte[] body;

private BufferedReader reader;

private ServletInputStream inputStream;

public ContentCachingRequestWrapper(HttpServletRequest request) throws IOException{
super(request);
loadBody(request);
}

private void loadBody(HttpServletRequest request) throws IOException{
body = IOUtils.toByteArray(request.getInputStream());
inputStream = new RequestCachingInputStream(body);
}

public byte[] getBody() {
return body;
}

@Override
public ServletInputStream getInputStream() throws IOException {
if (inputStream != null) {
return inputStream;
}
return super.getInputStream();
}

@Override
public BufferedReader getReader() throws IOException {
if (reader == null) {
reader = new BufferedReader(new InputStreamReader(inputStream, getCharacterEncodi
3ff0
ng()));
}
return reader;
}

private static class RequestCachingInputStream extends ServletInputStream {

private final ByteArrayInputStream inputStream;

public RequestCachingInputStream(byte[] bytes) {
inputStream = new ByteArrayInputStream(bytes);
}
@Override
public int read() throws IOException {
return inputStream.read();
}

@Override
public boolean isFinished() {
return inputStream.available() == 0;
}

@Override
public boolean isReady() {
return true;
}

@Override
public void setReadListener(ReadListener readlistener) {
}

}

}

https://github.com/sergewu/demo

THE END
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Spring