ServletRequest中getReader()和getInputStream()只能调用一次的解决办法

时间:2022-05-11 12:02:50

转载:http://blog.sina.com.cn/s/blog_870cd7b90101fg58.html

最近使用spring mvc做项目,数据格式是json,有一个功能是实现记录请求的参数,而请求的参数是整个RequestBody,Controller里是用过@RequestBody获取的。实现方法是通过一个Filter读取整个RequestBody并记录。但是这时就遇到一个问题,ServletRequest的getReader()和getInputStream()两个方法只能被调用一次,而且不能两个都调用。那么如果Filter中调用了一次,在Controller里面就不能再调用了。查看了下ServletRequest的说明,如下:

Java代码
  1. public ServletInputStream getInputStream() throws IOException;
  2. public BufferedReader getReader() throws IOException;

两个方法都注明方法只能被调用一次,由于RequestBody是流的形式读取,那么流读了一次就没有了,所以只能被调用一次。既然是因为流只能读一次的原因,那么只要将流的内容保存下来,就可以实现反复读取了。

实现方法:先将RequestBody保存为一个byte数组,然后通过Servlet自带的HttpServletRequestWrapper类覆盖getReader()和getInputStream()方法,使流从保存的byte数组读取。然后再Filter中将ServletRequest替换为ServletRequestWrapper。代码如下:

BodyReaderHttpServletRequestWrapper类包装ServletRequest,将流保存为byte[],然后将getReader()和getInputStream()方法的流的读取指向byte[]

Java代码
  1. import java.io.BufferedReader;
  2. import java.io.ByteArrayInputStream;
  3. import java.io.IOException;
  4. import java.io.InputStreamReader;
  5. import javax.servlet.ServletInputStream;
  6. import javax.servlet.http.HttpServletRequest;
  7. import javax.servlet.http.HttpServletRequestWrapper;
  8. import jodd.JoddDefault;
  9. import jodd.io.StreamUtil;
  10. public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper {
  11. private final byte[] body;
  12. public BodyReaderHttpServletRequestWrapper(HttpServletRequest request)
  13. throws IOException {
  14. super(request);
  15. body = StreamUtil.readBytes(request.getReader(), JoddDefault.encoding);
  16. }
  17. @Override
  18. public BufferedReader getReader() throws IOException {
  19. return new BufferedReader(new InputStreamReader(getInputStream()));
  20. }
  21. @Override
  22. public ServletInputStream getInputStream() throws IOException {
  23. final ByteArrayInputStream bais = new ByteArrayInputStream(body);
  24. return new ServletInputStream() {
  25. @Override
  26. public int read() throws IOException {
  27. return bais.read();
  28. }
  29. };
  30. }
  31. }

在Filter中将ServletRequest替换为ServletRequestWrapper

Java代码
    1. public class HttpServletRequestReplacedFilter implements Filter {
    2. @Override
    3. public void init(FilterConfig filterConfig) throws ServletException {
    4. //Do nothing
    5. }
    6. @Override
    7. public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {
    8. ServletRequest requestWrapper = null;
    9. if(request instanceof HttpServletRequest) {
    10. requestWrapper = new BodyReaderHttpServletRequestWrapper((HttpServletRequest) request);
    11. }
    12. if(null == requestWrapper) {
    13. chain.doFilter(request, response);
    14. } else {
    15. chain.doFilter(requestWrapper, response);
    16. }
    17. }
    18. @Override
    19. public void destroy() {
    20. //Do nothing
    21. }
    22. }