这篇文章主要介绍了如何构建可重复读取inputStream的request,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
目录
构建可重复读取inputStream的request
request中inputStream多次读取
原因
解决方法(缓存读取到的数据)
代码

构建可重复读取inputStream的request
我们知道,request的inputStream只能被读取一次,多次读取将报错,那么如何才能重复读取呢?答案之一是:增加缓冲,记录已读取的内容。

代码如下所示:

  1. import lombok.extern.log4j.Log4j2;
  2. import org.springframework.mock.web.DelegatingServletInputStream;
  3. import javax.servlet.ServletInputStream;
  4. import javax.servlet.http.HttpServletRequest;
  5. import javax.servlet.http.HttpServletRequestWrapper;
  6. import java.io.*;
  7. /**
  8. * request wrapper: 可重复读取request.getInputStream
  9. */
  10. @Log4j2
  11. public class RepeatedlyReadRequestWrapper extends HttpServletRequestWrapper {
  12. private static final int BUFFER_START_POSITION = 0;
  13. private static final int CHAR_BUFFER_LENGTH = 1024;
  14. /**
  15. * input stream 的buffer
  16. */
  17. private final String body;
  18. /**
  19. * @param request {@link javax.servlet.http.HttpServletRequest} object.
  20. */
  21. public RepeatedlyReadRequestWrapper(HttpServletRequest request) {
  22. super(request);
  23. StringBuilder stringBuilder = new StringBuilder();
  24. InputStream inputStream = null;
  25. try {
  26. inputStream = request.getInputStream();
  27. } catch (IOException e) {
  28. log.error("Error reading the request body…", e);
  29. }
  30. if (inputStream != null) {
  31. try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream))) {
  32. char[] charBuffer = new char[CHAR_BUFFER_LENGTH];
  33. int bytesRead;
  34. while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
  35. stringBuilder.append(charBuffer, BUFFER_START_POSITION, bytesRead);
  36. }
  37. } catch (IOException e) {
  38. log.error("Fail to read input stream",e);
  39. }
  40. } else {
  41. stringBuilder.append("");
  42. }
  43. body = stringBuilder.toString();
  44. }
  45. @Override
  46. public ServletInputStream getInputStream() throws IOException {
  47. final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());
  48. return new DelegatingServletInputStream(byteArrayInputStream);
  49. }
  50. }

接下来,需要一个对应的Filter.

代码如下所示:

  1. import javax.servlet.*;
  2. import javax.servlet.http.HttpServletRequest;
  3. import java.io.IOException;
  4. public class RepeatlyReadFilter implements Filter {
  5. @Override
  6. public void init(FilterConfig filterConfig) throws ServletException {
  7. //Do nothing
  8. }
  9. @Override
  10. public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
  11. if (request instanceof HttpServletRequest) {
  12. request = new RepeatedlyReadRequestWrapper((HttpServletRequest) request);
  13. }
  14. chain.doFilter(request, response);
  15. }
  16. @Override
  17. public void destroy() {
  18. //Do nothing
  19. }
  20. }

最后,需要在web.xml中,增加该Filter的配置(略)。

request中inputStream多次读取
在使用HTTP协议实现应用间接口通信时,服务端读取客户端请求过来的数据,会用到request.getInputStream(),第一次读取的时候可以读取到数据,但是接下来的读取操作都读取不到数据。

原因
一个InputStream对象在被读取完成后,将无法被再次读取,始终返回-1;

InputStream并没有实现reset方法(可以重置首次读取的位置),无法实现重置操作;

解决方法(缓存读取到的数据)
使用request、session等来缓存读取到的数据,这种方式很容易实现,只要setAttribute和getAttribute就行;

使用HttpServletRequestWrapper来包装HttpServletRequest,在中初始化读取request的InputStream数据,以byte[]形式缓存在其中,然后在Filter中将request转换为包装过的request;

代码
编写rHttpServletRequestWrapper子类,用来处理请求数据

  1. import java.io.BufferedReader;
  2. import java.io.ByteArrayInputStream;
  3. import java.io.IOException;
  4. import java.io.InputStreamReader;
  5. import java.nio.charset.Charset;
  6. import java.util.Enumeration;
  7. import javax.servlet.ReadListener;
  8. import javax.servlet.ServletInputStream;
  9. import javax.servlet.http.HttpServletRequest;
  10. import javax.servlet.http.HttpServletRequestWrapper;
  11. import lombok.extern.slf4j.Slf4j;
  12. @Slf4j
  13. public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper
  14. {
  15. private final byte[] body;
  16. public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException
  17. {
  18. super(request);
  19. Enumeration<String> e = request.getHeaderNames();
  20. while (e.hasMoreElements())
  21. {
  22. String name = (String) e.nextElement();
  23. String value = request.getHeader(name);
  24. log.debug("HttpServletRequest头信息:{}-{}", name, value);
  25. }
  26. body = HttpHelper.getBodyString(request).getBytes(Charset.forName("UTF-8"));
  27. }
  28. @Override
  29. public BufferedReader getReader() throws IOException
  30. {
  31. return new BufferedReader(new InputStreamReader(getInputStream()));
  32. }
  33. @Override
  34. public ServletInputStream getInputStream() throws IOException
  35. {
  36. final ByteArrayInputStream bais = new ByteArrayInputStream(body);
  37. return new ServletInputStream(){
  38. @Override
  39. public boolean isFinished()
  40. {
  41. return false;
  42. }
  43. @Override
  44. public boolean isReady()
  45. {
  46. return false;
  47. }
  48. @Override
  49. public void setReadListener(ReadListener listener)
  50. {
  51. }
  52. @Override
  53. public int read() throws IOException
  54. {
  55. return bais.read();
  56. }
  57. };
  58. }
  59. @Override
  60. public String getHeader(String name)
  61. {
  62. return super.getHeader(name);
  63. }
  64. @Override
  65. public Enumeration<String> getHeaderNames()
  66. {
  67. return super.getHeaderNames();
  68. }
  69. @Override
  70. public Enumeration<String> getHeaders(String name)
  71. {
  72. return super.getHeaders(name);
  73. }
  74. }

调用

  1. public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException
  2. {
  3. HttpServletRequest httpRequest = (HttpServletRequest) request;
  4. HttpServletResponse httpResponse = (HttpServletResponse) response;
  5. ServletRequest requestWrapper = null;
  6. requestWrapper = new BodyReaderHttpServletRequestWrapper(httpRequest);
  7. //数据读取处理
  8. //...
  9. //将requestWrapper专递给后面的过滤器
  10. filterChain.doFilter(requestWrapper, httpResponse);
  11. }

更多相关文章

  1. Android(安卓)jetpack Room数据库(一)基本使用
  2. android分组数据适配器demo
  3. android 之 读取本地json文件返回string
  4. android 数据库更改数据库位置【DbFlow示范】
  5. android之Gallery
  6. Android页面跳转是如何传递参数的
  7. android登录简单窗口
  8. ClickHouse镜像在阿里云镜像站首发上线
  9. SpringBoot项目实战之数据交互篇

随机推荐

  1. Linux下如何备份恢复和查看硬盘MBR与分区
  2. 008_Linux驱动之_IO口的配置
  3. 将JSON文件中的所有时间戳转换为bash(Ubu
  4. Linux时区设置和时间同步-基于CentOS 6(最
  5. VMware Workstation虚拟机平台安装Linux-
  6. 小记——inotify文件监控
  7. Linux技巧:如何利用Putty连接Linux主机
  8. Linux--常用命令--last
  9. 配置 limits.conf 限制 Linux 用户登录数
  10. 8.24 linux c socket 简单实现