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);
}