跳转到主内容
极星编程网:以代码为星,赴技术山海!

如何解决HttpServletRequest.getInputStream()多次读取问题

目录HttpServletRequest.getInputStream()多次读取问题解决方法缓存数据HttpServletRequestWrapper包装总结HttpServletRequest.getInputStream()多次读取问题使用POST方法发送数据时,我们习惯于把数据包装成json格式。

有些情况下,我们会在Filter中读取body数据进行数据校验,GET方法获取参数比较简单。

对于POST方法,可使用如下方法从request中获取body参数: 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)来存储数据。

1.获取到body后,直接缓存

String body = getBody(request);request.setAttribute("body", body);

优点:

方便

缺点:

不能控制第三方Filter

1.其他地方需要使用body时,只需调用

getAttribute

方法就能获取数据了:

request.getAttribute("body");HttpServletRequestWrapper包装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对象:

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()的机会了。

优点:

不影响第三方Filter

缺点:

多写了这么多代码,麻烦了一些总结以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

您可能感兴趣的文章:java通过HttpServletRequest获取post请求中的body内容的方法Java中的HttpServletRequest接口详细解读Java获取HttpServletRequest的三种方法详解HttpServletRequestWrapper干预Request处理流程解析SpringBoot获取HttpServletRequest的3种方式总结关于HttpServletRequest获取POST请求Body参数的3种方式

相关文章