好得很程序员自学网

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

Springboot如何设置过滤器及重复读取request里的body

需求:

request的content-type为applciation/json,进入controller之前需要把body中的参数取出来做一次处理,然后和hearder中的另一个参数做对比。

思路:

加一个过滤器,在过滤器中取出参数做处理,然后比较

注意:

body里的数据用流来读取,只能读取一次

HttpServletRequest的输入流只能读取一次的原因

我们先来看看为什么HttpServletRequest的输入流只能读一次,当我们调用getInputStream()方法获取输入流时得到的是一个InputStream对象,而实际类型是ServletInputStream,它继承于InputStream。

InputStream的read()方法内部有一个postion,标志当前流被读取到的位置,每读取一次,该标志就会移动一次,如果读到最后,read()会返回-1,表示已经读取完了。如果想要重新读取则需要调用reset()方法,position就会移动到上次调用mark的位置,mark默认是0,所以就能从头再读了。调用reset()方法的前提是已经重写了reset()方法,当然能否reset也是有条件的,它取决于markSupported()方法是否返回true。

InputStream默认不实现reset(),并且markSupported()默认也是返回false,这一点查看其源码便知:

我们再来看看ServletInputStream,可以看到该类没有重写mark(),reset()以及markSupported()方法:

综上,InputStream默认不实现reset的相关方法,而ServletInputStream也没有重写reset的相关方法,这样就无法重复读取流,这就是我们从request对象中获取的输入流就只能读取一次的原因。

重复读取body中数据的方法

这个自定义的requestWrapper继承了HttpServletRequestWrapper ,HttpServletRequestWrapper 是一个ServletRequest的包装类同时也是ServletRequest的实现类。

在这个自定义的requestWrapper里,用一个String做缓存,在构造方法里先把request的body中的数据缓存起来,然后重写了getInputStream,返回这个缓存的body,而不是从流中读取。

这样,在需要多次读取body的地方,只需要在过滤器中把原来的request换成这个自定义的request,然后把这个自定义的带缓存功能的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

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

public class BodyReaderRequestWrapper extends HttpServletRequestWrapper {

     private final String body;

 

     /**

      *

      * @param request

      */

     public BodyReaderRequestWrapper(HttpServletRequest request) throws IOException{

         super (request);

         StringBuilder sb = new StringBuilder();

         InputStream ins = request.getInputStream();

         BufferedReader isr = null ;

         try {

             if (ins != null ){

                 isr = new BufferedReader( new InputStreamReader(ins));

                 char [] charBuffer = new char [ 128 ];

                 int readCount = 0 ;

                 while ((readCount = isr.read(charBuffer)) != - 1 ){

                     sb.append(charBuffer, 0 ,readCount);

                 }

             } else {

                 sb.append( "" );

             }

         } catch (IOException e){

             throw e;

         } finally {

             if (isr != null ) {

                 isr.close();

             }

         }

 

         sb.toString();

         body = sb.toString();

     }

 

     @Override

     public BufferedReader getReader() throws IOException {

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

     }

 

     @Override

     public ServletInputStream getInputStream() throws IOException {

         final ByteArrayInputStream byteArrayIns = new ByteArrayInputStream(body.getBytes());

         ServletInputStream servletIns = new ServletInputStream() {

             @Override

             public boolean isFinished() {

                 return false ;

             }

 

             @Override

             public boolean isReady() {

                 return false ;

             }

 

             @Override

             public void setReadListener(ReadListener readListener) {

 

             }

 

             @Override

             public int read() throws IOException {

                 return byteArrayIns.read();

             }

         };

         return   servletIns;

     }

}

springboot的过滤器

2个注解:

@WebFilter (过滤器上) @ServletComponentScan (加在启动类上,支持servlet components扫描(为了webfilter))

?

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

@Order ( 999 ) // 序号越低,优先级越高

// 加上WebFilter即可成为过滤器

@WebFilter (filterName= "myFilter" , urlPatterns= "/api/workorder/service/selfAppeal" )

public class ExternalFilter implements Filter  {

 

     private final static Logger logger = LoggerFactory.getLogger(ExternalFilter. class );   

     @Override

     public void init(FilterConfig filterConfig) throws ServletException {

         logger.info( "filter init" );

     }

 

     @Override

     public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)

             throws IOException, ServletException {

         ResponseObject object = new ResponseObject();

         HttpServletRequest req = (HttpServletRequest)servletRequest;

         HttpServletResponse res = (HttpServletResponse)servletResponse;

         // 一个request的包装类,初始化时缓存了body,重写了getInputStream返回缓存的body,实现重复读取body

         BodyReaderRequestWrapper requestWrapper  = new BodyReaderRequestWrapper(req);

         String requestURI = requestWrapper.getRequestURI();

         System.out.println( "--------------------->过滤器:请求地址" + requestURI);

         String md5 = requestWrapper.getHeader( "md5" )  ;

       

         if (md5 == null || !md5.toLowerCase().equals(MD5.md5(ReqGetBody.getBody(requestWrapper)).toLowerCase())) {

             object.setStatus( 501 );

             object.setStatusText( "数据md5校验失败" );

             render(object, res);

             return ;

         }

         // 这里传递下去的就是自定义的request了,所以后续的Controller才能重复读取到body里的参数

         filterChain.doFilter(requestWrapper, res);

     }

 

     @Override

     public void destroy() {

     }

 

     /**

     * @Title: render

     * @Description: 发送Response

     * @param object

     * @param response void

     * @author MasterYi

     * @date 2020年1月15日上午10:48:45

     */

     private void render(ResponseObject object, HttpServletResponse response) {

         response.setContentType( "application/json;charset=UTF-8" );

         try {

             response.setStatus( 200 );

             response.getWriter().write(JSONObject.toJSON(object).toString());

         } catch (IOException e) {

             logger.error( "ExternalFilter写入response异常" );

         }

     }

}

上面的getBody的代码

从body中取值的具体操作

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

public class ReqGetBody {

     static public String getBody(HttpServletRequest request) {

         try {

             ServletInputStream in = request.getInputStream();

             String body;

             body = StreamUtils.copyToString(in, Charset.forName( "UTF-8" ));

            

             return body;

         } catch (IOException e) {

             e.printStackTrace();

             return "" ;

         }

     }

}

以上为个人经验,希望能给大家一个参考,也希望大家多多支持。

原文链接:https://blog.csdn.net/qq_34548229/article/details/104014374

查看更多关于Springboot如何设置过滤器及重复读取request里的body的详细内容...

  阅读:23次