好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

为了多次读取ServletInputStream引发的一系列问题

多次读取ServletInputStream引发的问题

因为服务器和app之间传输方式是JSON

格式如下

?

1

2

3

4

5

{

  head: null

body: null

token:xxxxxxxxxxxxxxxxxxxxx

}

所以想在服务端写个拦截器或者filter先读取token,来验证身份.但是如果前置拦截,那么在springMVC里的参数控制器就会读取不到,导致@RequestBody 这个注解的参数不起作用.

原因在于ServletInputStream已经在前置拦截器里读取过一次,在接下来的ArgumentHandler里读取的时候就不能再读了,导致取不到数据而无法赋值.

于是写了下面的filter来让ServletInputStream来多次读取

先写个request

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

public class MyHttpRequest extends HttpServletRequestWrapper {

  private static Logger log=Logger.getLogger(MyHttpRequest. class );

     private   byte [] bytes;

     /**

      * @param request {@link javax.servlet.http.HttpServletRequest} object.

      * @throws IOException

      */

     public MyHttpRequest(HttpServletRequest request) throws IOException {

         super (request);

         bytes= IOUtils.toByteArray( request.getInputStream());     

     }

 

     @Override

     public ServletInputStream getInputStream() throws IOException {

         final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);

         return new DelegatingServletInputStream(byteArrayInputStream);

     }

     @Override

     public BufferedReader getReader() throws IOException {

      return new BufferedReader( new InputStreamReader(getInputStream()));

     }

以上内容是先读取出来,然后放到一个byte[]里,以后每次拿流的时候就可以再新建一个关于Byte[]的流.

然后再写一个filter

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

public class MyFilter implements Filter{

  @Override

  public void destroy() {   

  }

 

  @Override

  public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)

    throws IOException, ServletException {

   HttpServletRequest request=(HttpServletRequest) req;

   MyHttpRequest myrequest= new MyHttpRequest(request);

   chain.doFilter(myrequest, res); 

  }

 

  @Override

  public void init(FilterConfig arg0) throws ServletException { 

  }

}

在web.xml里配置一下,试试效果,非常不错.可以多次读取了,

但是,我去

在post请求下,request.getParamter这种东西又不起作用了.

原因在于我们重写的那个request是默认调用父类的super.getparamter方法,而super.getparamter又依赖于super.getInputStream,但是在我们重写的request里的构造方法里,第一句话就老request注入到super里,然后下一句话就把老的request的流给读了,导致我们调用super.getparameter的时候遇到了一个空的流。

解决思路:自己解决

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

package com.hrhs.jyj.filter;

import java.io.BufferedReader;

import java.io.ByteArrayInputStream;

import java.io.IOException;

import java.io.InputStreamReader;

import java.io.UnsupportedEncodingException;

import java.util.ArrayList;

import java.util.Enumeration;

import java.util.HashMap;

import java.util.List;

import java.util.Map;

import javax.servlet.ServletInputStream;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletRequestWrapper;

import org.apache测试数据mons.io.IOUtils;

import org.apache.log4j.Logger;

import org.springframework.mock.web.DelegatingServletInputStream;

 

public class MyHttpRequest extends HttpServletRequestWrapper {

  private static Logger log = Logger.getLogger(MyHttpRequest. class );

  private byte [] bytes;

  private String body;

  private Map<String, List<String>> map;

  private int readMap= 0 ;

  private String queryString;

  /**

   * @param request

   *            {@link javax.servlet.http.HttpServletRequest} object.

   * @throws IOException

   */

  public MyHttpRequest(HttpServletRequest request) throws IOException {

   super (request);

   bytes = IOUtils.toByteArray(request.getInputStream());

   queryString = request.getQueryString();

  

  }

 

  @Override

  public ServletInputStream getInputStream() throws IOException {

   final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);

   return new DelegatingServletInputStream(byteArrayInputStream);

  }

 

  @Override

  public BufferedReader getReader() throws IOException {

   return new BufferedReader( new InputStreamReader(getInputStream()));

  }

 

  @Override

  public String getParameter(String name) {

   log.info( "走了getParameter" );

   return super .getParameter(name);

  }

 

  @Override

  public Map<String, String[]> getParameterMap() {

   log.info( "走了getParameterMap" );

   return super .getParameterMap();

  }

 

  @Override

  public Enumeration<String> getParameterNames() {

   log.info( "走了getParameterNames" );

   return super .getParameterNames();

  }

  //暂时先改写这一个,其他的也可以修改

  @Override

  public String[] getParameterValues(String name) {

   log.info( "走了getParameterValues" );

   try {

    Map<String, List<String>> nameVals = doParameter();

    List<String> list = nameVals.get(name);

    if (list!= null &&list.size()> 0 ){

     return list.toArray( new String[]{});

    }

   } catch (UnsupportedEncodingException e) {

    e.printStackTrace();

   }

   return new String[]{};

  }

  //这里获取所有参数值的map

  public Map<String, List<String>> doParameter() throws UnsupportedEncodingException {

   if (readMap== 0 ){

    //这里把post里的参数和地址栏参数结合到一起,然后解析

    body = new String(bytes, getCharacterEncoding())+ "&" +queryString;

    String[] nameVals = body.split( "&" );

    map = new HashMap<String, List<String>>();

    for (String nameVal : nameVals) {

     String name = nameVal.split( "=" )[ 0 ];

     String val = nameVal.split( "=" )[ 1 ];

     if (map.containsKey(name)) {

      List<String> vals = map.get(name);

      vals.add(val);

      map.put(name, vals);

     } else {

      List<String> vals = new ArrayList<String>();

      vals.add(val);

      map.put(name, vals);

     }

    }

    readMap= 1 ;

   }

   return map; 

  }

}

ServletInputStream重复读取问题

本来是想实现tomcat的acess_log打印post请求参数。吐槽一下,tomcat功能和nginx差了好几条街。网上找了个方法,用tomcat的filter来实现。

但是,写filter的时候,发现了ServletInputStream重复读取问题。

网上找个几个方法,都不能直接用。综合网上的资料,根据自己的理解,终于能完美运行了。

直接贴代码了,亲测能用

首先要写个BufferHttpServletRequestWrapper类,用来复制HttpServletRequest request。

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

import java.io.BufferedReader;

import java.io.ByteArrayInputStream;

import java.io.ByteArrayOutputStream;

import java.io.IOException;

import java.io.InputStream;

import java.io.InputStreamReader;

import javax.servlet.ServletInputStream;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletRequestWrapper;

 

public class BufferHttpServletRequestWrapper extends HttpServletRequestWrapper {

  private final byte [] body;

  public BufferHttpServletRequestWrapper(HttpServletRequest request) throws IOException {

   super (request);

   InputStream is = request.getInputStream();

         ByteArrayOutputStream baos = new ByteArrayOutputStream();

         byte buff[] = new byte [ 1024 ];

         int read;

         while ( ( read = is.read( buff ) ) > 0 ) {

             baos.write( buff, 0 , read );

         }

         body = baos.toByteArray();

  }

 

  @Override

  public BufferedReader getReader() throws IOException {

   return new BufferedReader( new InputStreamReader(getInputStream()));

  }

 

  @Override

  public ServletInputStream getInputStream() throws IOException {

   final ByteArrayInputStream bais = new ByteArrayInputStream(body);

   return new ServletInputStream() {

 

    @Override

    public int read() throws IOException {

     return bais.read();

    }

   };

  }

}

然后filter实现如下:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

import java.io.ByteArrayOutputStream;

import java.io.IOException;

import java.io.InputStream;

import javax.servlet.Filter;

import javax.servlet.FilterChain;

import javax.servlet.FilterConfig;

import javax.servlet.ServletException;

import javax.servlet.ServletRequest;

import javax.servlet.ServletResponse;

import javax.servlet.http.HttpServletRequest;

 

/**

  * Servlet Filter implementation class PostDataDumperFilter

  */

 

public class PostDataDumperFilter implements Filter {

 

  private FilterConfig filterConfig = null ;

 

     /**

      * Default constructor.

      */

     public PostDataDumperFilter() {

         // TODO Auto-generated constructor stub

     }

 

  /**

   * @see Filter#destroy()

   */

  public void destroy() {

   this .filterConfig = null ;

  }

 

  /**

   * @see Filter#doFilter(ServletRequest, ServletResponse, FilterChain)

   */

  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

   if (filterConfig == null )

    return ;

   //备份HttpServletRequest

   ServletRequest requestWrapper = null ; 

         if (request instanceof HttpServletRequest) { 

             requestWrapper = new BufferHttpServletRequestWrapper((HttpServletRequest) request); 

         }

   //使用流

   InputStream reader = requestWrapper.getInputStream();

   ByteArrayOutputStream byteOutput = new ByteArrayOutputStream( 100 );

   int i = 0 ;

   byte [] b = new byte [ 100 ];

   while ((i = reader.read(b))!= - 1 ){

    byteOutput.write(b, 0 , i);

   }

   request.setAttribute( "post" , new String(byteOutput.toByteArray()));

   // pass the request along the filter chain

   if ( null == requestWrapper){

    chain.doFilter(request, response);

   } else { 

             chain.doFilter(requestWrapper, response); 

         }

  }

 

  /**

   * @see Filter#init(FilterConfig)

   */

  public void init(FilterConfig fConfig) throws ServletException {

   this .filterConfig = fConfig;

  }

}

好了,就写到这里。以上为个人经验,希望能给大家一个参考,也希望大家多多支持。

原文链接:https://blog.csdn.net/dmw412724/article/details/79296345

查看更多关于为了多次读取ServletInputStream引发的一系列问题的详细内容...

  阅读:27次