Json已经成为一种主流的数据传输格式,请求参数是整个RequestBody。
那么,拦截器中如何获取Json呢?
办法是:通过request.getInputStream。

RequestBody是流的形式读取,流读了一次就没有了,所以只能被调用一次。
在拦截器中读取后,Controller中,通过@RequestBody注解获取Json就会失败。
那么,问题来了,如何在拦截器中获取json后,同时可以在Controller再次获取呢?
办法是:在使用之前将流储存在一个能持续request生命周期的元素中。

下面是一个完整的通过例子,包含了如下知识点:
1、Spring拦截器的使用
2、注解方式判定是否被拦截
3、在拦截器中获取request中的Json格式参数
4、解决RequestBody只能读取一次的问题

一、创建一个自定义基础拦截器抽象类,以及用作拦截判定的注解。


[java]  view plain  copy



1. package com.tcl.shbc.thirdbus.interceptor;  
2.   
3. import java.lang.annotation.ElementType;  
4. import java.lang.annotation.Retention;  
5. import java.lang.annotation.RetentionPolicy;  
6. import java.lang.annotation.Target;  
7.   
8. import javax.servlet.http.HttpServletRequest;  
9. import javax.servlet.http.HttpServletResponse;  
10.   
11. import org.springframework.web.method.HandlerMethod;  
12. import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;  
13.   
14. /**
15.  * 自定义Base拦截器
16.  * @author Oliver
17.  * @version 20161214
18.  */  
19. public abstract class BaseInterceptor extends HandlerInterceptorAdapter {  
20.   
21. public String failed;  
22.       
23. public String name;  
24.       
25. /**
26.      * 自定义拦截逻辑
27.      * 
28.      * @param handler
29.      * @return 是否拦截,false拦截,true:不拦截;
30.      */  
31. public boolean isMyHandler(Object handler) {  
32. // 判断是否应该被拦截  
33. if (!(handler instanceof HandlerMethod)) {  
34. return true;  
35.         }  
36.           
37.         HandlerMethod handlerMethod = (HandlerMethod) handler;  
38. class);  
39. // 只有被@Interceptor注解的类才会被拦截  
40. if (interceptor == null){  
41. return true;  
42.         }  
43.         failed = interceptor.failed();  
44.         name = interceptor.name();  
45.   
46. return false;  
47.     }  
48.   
49. /**
50.      * 重写preHandle方法
51.      */  
52. @Override  
53. public boolean preHandle(  
54.             HttpServletRequest request,  
55.             HttpServletResponse response,  
56. throws Exception {  
57. if (!isMyHandler(handler)) {  
58. return doHandler(response, runHandler(request, response));  
59.         }  
60. return super.preHandle(request, response, handler);  
61.     }  
62.   
63. public abstract boolean runHandler(HttpServletRequest request, HttpServletResponse response);  
64.   
65. /**
66.      * 根据运行结果做相应的处理
67.      * 
68.      * @param response
69.      * @param isInterceptor
70.      *         是否拦截,false拦截,true:不拦截;
71.      * @return 是否拦截,false拦截,true:不拦截;
72.      * @throws Exception
73.      */  
74. public boolean doHandler(HttpServletResponse response, boolean isInterceptor) throws Exception {  
75. if (!isInterceptor) {  
76. if (!failed.equals("")) {  
77. // to do something  
78.             }  
79. return false;  
80.         }  
81. return true;  
82.     }  
83.   
84. /**
85.      * 自定义拦截器配套注解
86.      * @author Oliver
87.      * @version 20161214
88.      */  
89. @Target(ElementType.METHOD)  
90. @Retention(RetentionPolicy.RUNTIME)  
91. public static @interface Interceptor {  
92.           
93. // 根据业务需求确定用途,也可省略,例如log打印,方法判定等  
94. public String name();  
95.           
96. // 根据业务需求确定用途,也可省略,例如指定失败跳转,或者失败后to do something  
97. public String failed() default "";  
98.     }  
99. }




二、创建具体拦截业务逻辑类


[java]  view plain  copy




1. package com.tcl.shbc.thirdbus.invoke.interceptor;  
2.   
3. import java.util.HashMap;  
4. import java.util.Map;  
5.   
6. import javax.servlet.http.HttpServletRequest;  
7. import javax.servlet.http.HttpServletResponse;  
8.   
9. import org.apache.logging.log4j.LogManager;  
10. import org.apache.logging.log4j.Logger;  
11.   
12. import com.alibaba.fastjson.JSON;  
13. import com.tcl.shbc.thirdbus.contants.InvokeAuthProperties;  
14. import com.tcl.shbc.thirdbus.contants.MessagesEnum;  
15. import com.tcl.shbc.thirdbus.interceptor.BaseInterceptor;  
16. import com.tcl.shbc.thirdbus.interceptor.MyHttpServletRequestWrapper;  
17. import com.tcl.shbc.thirdbus.utils.GetRequestJsonUtils;  
18. import com.tcl.shbc.thirdbus.utils.MD5Util;  
19.   
20. public class AuthInterceptor extends BaseInterceptor{  
21.   
22. static Logger logger = LogManager.getLogger(AuthInterceptor.class);  
23.   
24. @Override  
25. public boolean runHandler(HttpServletRequest request,  
26.             HttpServletResponse response) {  
27. try {  
28. new MyHttpServletRequestWrapper(request);  
29.             String jsonStr = GetRequestJsonUtils.getRequestJsonString(myWrapper);  
30.             。。。


[java]  view plain  copy




    1. <span style="white-space:pre;">         </span>return true;  
    2. catch (Exception e) {  
    3.             MessagesEnum.errorSet(response, MessagesEnum.ERROR_1101);  
    4.         }  
    5. return false;  
    6.     }  
    7. }





    三、创建获取request中json字符串的内容的工具类


    [java]  view plain  copy




    1. package com.tcl.shbc.thirdbus.utils;  
    2.   
    3. import java.io.IOException;  
    4.   
    5. import javax.servlet.http.HttpServletRequest;  
    6.   
    7. public class GetRequestJsonUtils {  
    8. /***
    9.      * 获取 request 中 json 字符串的内容
    10.      * 
    11.      * @param request
    12.      * @return : <code>byte[]</code>
    13.      * @throws IOException
    14.      */  
    15. public static String getRequestJsonString(HttpServletRequest request)  
    16. throws IOException {  
    17.         String submitMehtod = request.getMethod();  
    18. // GET  
    19. if (submitMehtod.equals("GET")) {  
    20. return new String(request.getQueryString().getBytes("iso-8859-1"),"utf-8").replaceAll("%22", "\"");  
    21. // POST  
    22. else {  
    23. return getRequestPostStr(request);  
    24.         }  
    25.     }  
    26.   
    27. /**   
    28.      * 描述:获取 post 请求的 byte[] 数组
    29.      * <pre>
    30.      * 举例:
    31.      * </pre>
    32.      * @param request
    33.      * @return
    34.      * @throws IOException    
    35.      */  
    36. public static byte[] getRequestPostBytes(HttpServletRequest request)  
    37. throws IOException {  
    38. int contentLength = request.getContentLength();  
    39. if(contentLength<0){  
    40. return null;  
    41.         }  
    42. byte buffer[] = new byte[contentLength];  
    43. for (int i = 0; i < contentLength;) {  
    44.   
    45. int readlen = request.getInputStream().read(buffer, i,  
    46.                     contentLength - i);  
    47. if (readlen == -1) {  
    48. break;  
    49.             }  
    50.             i += readlen;  
    51.         }  
    52. return buffer;  
    53.     }  
    54.   
    55. /**   
    56.      * 描述:获取 post 请求内容
    57.      * <pre>
    58.      * 举例:
    59.      * </pre>
    60.      * @param request
    61.      * @return
    62.      * @throws IOException    
    63.      */  
    64. public static String getRequestPostStr(HttpServletRequest request)  
    65. throws IOException {  
    66. byte buffer[] = getRequestPostBytes(request);  
    67.         String charEncoding = request.getCharacterEncoding();  
    68. if (charEncoding == null) {  
    69. "UTF-8";  
    70.         }  
    71. return new String(buffer, charEncoding);  
    72.     }  
    73. }


    四、重写HttpServletRequestWrapper方法


    [java]  view plain  copy




    1. package com.tcl.shbc.thirdbus.interceptor;  
    2.   
    3. import java.io.BufferedReader;  
    4. import java.io.ByteArrayInputStream;  
    5. import java.io.IOException;  
    6. import java.io.InputStreamReader;  
    7.   
    8. import javax.servlet.ServletInputStream;  
    9. import javax.servlet.http.HttpServletRequest;  
    10. import javax.servlet.http.HttpServletRequestWrapper;  
    11.   
    12. import org.springframework.util.StreamUtils;  
    13.   
    14. /**
    15.  * 重写HttpServletRequestWrapper方法
    16.  * @author Oliver
    17.  * @version 20161214
    18.  */  
    19. public class MyHttpServletRequestWrapper extends HttpServletRequestWrapper {  
    20. private byte[] requestBody = null;  
    21.   
    22. public MyHttpServletRequestWrapper (HttpServletRequest request) {  
    23.   
    24. super(request);  
    25.   
    26. //缓存请求body  
    27. try {  
    28.             requestBody = StreamUtils.copyToByteArray(request.getInputStream());  
    29. catch (IOException e) {  
    30.             e.printStackTrace();  
    31.         }  
    32.     }  
    33.   
    34. /**
    35.      * 重写 getInputStream()
    36.      */  
    37. @Override  
    38. public ServletInputStream getInputStream() throws IOException {  
    39. if(requestBody == null){  
    40. new byte[0];  
    41.         }  
    42. final ByteArrayInputStream bais = new ByteArrayInputStream(requestBody);  
    43. return new ServletInputStream() {  
    44. @Override  
    45. public int read() throws IOException {  
    46. return bais.read();  
    47.             }  
    48.         };  
    49.     }  
    50.   
    51. /**
    52.      * 重写 getReader()
    53.      */  
    54. @Override  
    55. public BufferedReader getReader() throws IOException {  
    56. return new BufferedReader(new InputStreamReader(getInputStream()));  
    57.     }  
    58. }


    五、创建一个实现Filter的类,重写doFilter方法,将ServletRequest替换为自定义的request类


    [java]  view plain  copy




      1. package com.tcl.shbc.thirdbus.interceptor;  
      2.   
      3. import java.io.IOException;  
      4.   
      5. import javax.servlet.Filter;  
      6. import javax.servlet.FilterChain;  
      7. import javax.servlet.FilterConfig;  
      8. import javax.servlet.ServletException;  
      9. import javax.servlet.ServletRequest;  
      10. import javax.servlet.ServletResponse;  
      11. import javax.servlet.http.HttpServletRequest;  
      12.   
      13. /**
      14.  * 创建一个实现Filter的类,重写doFilter方法,将ServletRequest替换为自定义的request类
      15.  * @author Oliver
      16.  * @version 20161214
      17.  */  
      18. public class HttpServletRequestReplacedFilter implements Filter {  
      19.   
      20. @Override  
      21. public void destroy() {  
      22. // TODO Auto-generated method stub  
      23.     }  
      24.   
      25. @Override  
      26. public void doFilter(ServletRequest request, ServletResponse response,  
      27. throws IOException, ServletException {  
      28. null;  
      29. if(request instanceof HttpServletRequest) {  
      30. new MyHttpServletRequestWrapper((HttpServletRequest) request);  
      31.         }  
      32. if(requestWrapper == null) {  
      33.             chain.doFilter(request, response);  
      34. else {  
      35.             chain.doFilter(requestWrapper, response);  
      36.         }     
      37.     }   
      38.       
      39.   
      40. @Override  
      41. public void init(FilterConfig arg0) throws ServletException {  
      42. // TODO Auto-generated method stub  
      43.           
      44.     }  
      45. }


      六、在配置文件中添加<filter>


      [html]  view plain  copy



      1. <filter>  
      2. <filter-name>requestFilter</filter-name>  
      3. <filter-class>com.tcl.shbc.thirdbus.interceptor.HttpServletRequestReplacedFilter</filter-class>  
      4. </filter>  
      5. <filter-mapping>  
      6. <filter-name>requestFilter</filter-name>
      7. <url-pattern>*.do</url-pattern>  
      8. </filter-mapping>



      个人总结:
             第一点:其中过滤器的主要功能就是将InputStream流中的内容缓存起来,因为这个流只可以访问一次,如果在拦截器这里用流的形式将它读出来是没问题的,但是在Controller中就没办法取到所以报错。
             第二点:如果在过滤器中已经配置,其实在拦截器中也就不必再调用一遍,因为它已经缓存过,直接从改造过得getInputStream中拿就行了,

      /**
           * 从流中获取数据,request已经重写。详尽MyHttpServletRequestWrapper
           * @param request
           * @return
           * @throws IOException
           */
          private String getParameter(HttpServletRequest request) throws IOException
          {
      
              int contentLength = request.getContentLength();
              if (contentLength < 0)
              {
                  return null;
              }
              byte buffer[] = new byte[contentLength];
              for (int i = 0; i < contentLength;)
              {
                  int readlen = request.getInputStream().read(buffer, i, contentLength - i);
                  if (readlen == -1)
                  {
                      break;
                  }
                  i += readlen;
              }
              return new String(buffer);
          }