目录
背景
坑点介绍
解决方法
背景
我们在做接口调用的时候使用POST方法发送数据,我们习惯于把数据包装成json格式。有些情况下,我们会在Filter中读取请求数据进行数据校验,常用的Http请求方式主要有两种,对于GET方式获取参数比较简单;对于POST方法,我们获取Body的中的数据就会有一个非常大的坑。
坑点介绍
看了Spring的底层源码,我们发现对于POST方法,可使用如下方法从Request中获取Body参数:
代码块
Java
private String getBody(HttpServletRequest request) throws IOException
{
InputStream in = request.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(in,
Charset.forName("UTF-8")));
StringBuffer sb = new StringBuffer("");
String temp;
while ((temp = br.readLine()) != null) {
sb.append(temp);
}
if (in != null) {
in.close();
}
if (br != null) {
br.close();
}
return sb.toString();
}
注意,这里有了一次request.getInputStream()调用。 但是在测试时,一直报JSON格式不正确的错误。经调查发现,项目中使用了公司基础组件中的Filter,而该Filter中也解析了body。同时,不出所料,也是通过调用getInputStream()方法获取的。
原来: 一个InputStream对象在被读取完成后,将无法被再次读取,始终返回-1; InputStream并没有实现reset方法(可以重置首次读取的位置),无法实现重置操作; 因此,当自己写的Filter中调用了一次getInputStream()后,后面再调用getInputStream()读取的数据都为空,所以才报JSON格式不正确的错误。
解决方法
使用HttpServletRequestWrapper进行包装缓存数据
所谓缓存数据,其实就是调用ServletRequest的setAttribute(String s, Object o)来存储数据。 获取到body后,直接缓存 String body = getBody(request); request.setAttribute("body", body);
使用HttpServletRequestWrapper进行包装缓存数据
所谓缓存数据,其实就是调用ServletRequest的setAttribute(String s, Object o)来存储数据。 获取到body后,直接缓存 String body = getBody(request); request.setAttribute("body", body);
优点:方便缺点:不能控制第三方Filter 其他地方需要使用body时,只需调用getAttribute方法就能获取数据了: request.getAttribute("body");
HttpServletRequestWrapper包装方法:
Java
public class RequestWrapper extends HttpServletRequestWrapper {
private final byte[] body;
public RequestWrapper(HttpServletRequest request) throws IOException {
super(request);
body = getBodyStringFromReq(request).getBytes(Charset.forName("UTF-8"));
}
public String getBodyString() {
try {
return new String(body, "UTF-8");
} catch (UnsupportedEncodingException ex) {
return new String(body);
}
}
private String getBodyStringFromReq(ServletRequest request) {
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();
}
}
在Filter中使用时,FilterChain.doFilter()传入Wrapper对象:
代码块
Java
public class TestFilter implements Filter {
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
RequestWrapper requestWrapper = new RequestWrapper((HttpServletRequest)request);
String body = requestWrapper.getBodyString();
chain.doFilter(requestWrapper, response);
//传入Wrapper对象
}
@Override
public void init(FilterConfig arg0) throws ServletException {
}
}
这样,位于后面的Filter就可以拥有唯一一次调用HttpServletRequest.getInputStream()的机会了。



