拦截请求返回值的方法

ゝ一世哀愁。 2022-07-15 07:43 791阅读 0赞

在web应用开发时,我们经常会需要拦截请求,在请求前后做一些事,比如打印日志等,这种方式就相当于spring中的面向切面编程,以下总结了下实现这种方式的一些方法。

###1. 使用拦截器+过滤器

这种方法是目前比较通用的方法,不受限于框架限制,不管是用SSH还是springmvc都可以采用,实现方式大致如下:
(1)定义一个拦截器,比如WebRequestInterceptor,在preHandle里面拦截request,从中取出参数等信息,然后打印出来即可。
(2)定义一个过滤器,比如WebResponseFilter,在doFilter里面使用HttpServletResponseWrapper包装传进来的response,然后重写一些方法即可实现。
(注意:这里有个坑,就是直接使用拦截器无法从response里面取出返回值!)
(3)相关代码大致如下:

WebInterceptor.java

  1. package com.zuolin.interceptor;
  2. import java.util.Enumeration;
  3. import javax.servlet.http.HttpServletRequest;
  4. import javax.servlet.http.HttpServletResponse;
  5. import org.slf4j.Logger;
  6. import org.slf4j.LoggerFactory;
  7. import org.springframework.web.servlet.HandlerInterceptor;
  8. import org.springframework.web.servlet.ModelAndView;
  9. import com.zuolin.filter.WrapperResponse;
  10. public class WebInterceptor implements HandlerInterceptor {
  11. private static final Logger logger = LoggerFactory.getLogger(WebInterceptor.class);
  12. @Override
  13. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object object) throws Exception {
  14. if (logger.isDebugEnabled()) {
  15. StringBuffer sb = new StringBuffer();
  16. sb.append("{");
  17. Enumeration<String> headers = request.getHeaderNames();
  18. int i = 0;
  19. while (headers.hasMoreElements()) {
  20. String header = headers.nextElement();
  21. if (i > 0)
  22. sb.append(", ");
  23. sb.append(header + ": " + request.getHeader(header));
  24. i++;
  25. }
  26. sb.append("}");
  27. logger.debug("Pre handling request: {}, headers: {}", getRequestInfo(request, true), sb.toString());
  28. }
  29. return true;
  30. }
  31. private static String getRequestInfo(HttpServletRequest request, boolean requestDetails) {
  32. StringBuffer sb = new StringBuffer();
  33. sb.append(request.getMethod()).append(" ");
  34. sb.append(request.getRequestURI());
  35. if (requestDetails) {
  36. Enumeration<String> e = request.getParameterNames();
  37. sb.append("{");
  38. int i = 0;
  39. while (e.hasMoreElements()) {
  40. String name = e.nextElement();
  41. String val = request.getParameter(name);
  42. if (val != null && !val.isEmpty()) {
  43. if (i > 0)
  44. sb.append(", ");
  45. sb.append(name).append(": ").append(val);
  46. i++;
  47. }
  48. }
  49. sb.append("}");
  50. }
  51. return sb.toString();
  52. }
  53. @Override
  54. public void postHandle(HttpServletRequest request, HttpServletResponse response, Object object,
  55. ModelAndView modelAndView) throws Exception {
  56. }
  57. @Override
  58. public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object object,
  59. Exception exception) throws Exception {
  60. String requestInfo = getRequestInfo(request, false);
  61. if (logger.isDebugEnabled()) {
  62. logger.debug("Complete request: {}", requestInfo);
  63. }
  64. }
  65. }

WrapperOutputStream.java

  1. package com.zuolin.filter;
  2. import java.io.ByteArrayOutputStream;
  3. import java.io.IOException;
  4. import javax.servlet.ServletOutputStream;
  5. public class WrapperOutputStream extends ServletOutputStream{
  6. private ByteArrayOutputStream bos;
  7. public WrapperOutputStream(ByteArrayOutputStream bos) {
  8. this.bos = bos;
  9. }
  10. @Override
  11. public void write(int b) throws IOException {
  12. bos.write(b);
  13. }
  14. }

WrapperResponse.java

  1. package com.zuolin.filter;
  2. import java.io.ByteArrayOutputStream;
  3. import java.io.IOException;
  4. import java.io.OutputStreamWriter;
  5. import java.io.PrintWriter;
  6. import java.io.UnsupportedEncodingException;
  7. import javax.servlet.ServletOutputStream;
  8. import javax.servlet.http.HttpServletResponse;
  9. import javax.servlet.http.HttpServletResponseWrapper;
  10. public class WrapperResponse extends HttpServletResponseWrapper{
  11. private ByteArrayOutputStream buffer;
  12. private ServletOutputStream out;
  13. public WrapperResponse(HttpServletResponse response) throws UnsupportedEncodingException {
  14. super(response);
  15. buffer = new ByteArrayOutputStream();
  16. out = new WrapperOutputStream(buffer);
  17. }
  18. @Override
  19. public ServletOutputStream getOutputStream() throws IOException {
  20. return out;
  21. }
  22. @Override
  23. public void flushBuffer() throws IOException {
  24. if (out != null) {
  25. out.flush();
  26. }
  27. }
  28. public byte[] getContent() throws IOException {
  29. flushBuffer();
  30. return buffer.toByteArray();
  31. }
  32. }

ResponseFilter.java

  1. package com.zuolin.filter;
  2. import java.io.IOException;
  3. import javax.servlet.Filter;
  4. import javax.servlet.FilterChain;
  5. import javax.servlet.FilterConfig;
  6. import javax.servlet.ServletException;
  7. import javax.servlet.ServletOutputStream;
  8. import javax.servlet.ServletRequest;
  9. import javax.servlet.ServletResponse;
  10. import javax.servlet.http.HttpServletResponse;
  11. import org.slf4j.Logger;
  12. import org.slf4j.LoggerFactory;
  13. public class ResponseFilter implements Filter {
  14. private static final Logger logger = LoggerFactory.getLogger(ResponseFilter.class);
  15. @Override
  16. public void destroy() {
  17. }
  18. @Override
  19. public void doFilter(ServletRequest request, ServletResponse response,
  20. FilterChain filterChain) throws IOException, ServletException {
  21. WrapperResponse wrapperResponse = new WrapperResponse((HttpServletResponse) response);
  22. filterChain.doFilter(request, wrapperResponse);
  23. byte[] content = wrapperResponse.getContent();
  24. if (logger.isDebugEnabled() && content != null && content.length > 0) {
  25. logger.debug("Response Content: {}", new String(content));
  26. }
  27. ServletOutputStream out = response.getOutputStream();
  28. out.write(content);
  29. out.flush();
  30. }
  31. @Override
  32. public void init(FilterConfig paramFilterConfig) throws ServletException {
  33. }
  34. }

然后记得在web.xml或spring的配置文件中加上以上类的定义就可以直接使用了。

这种方式有个缺陷,就是要写很多代码,而且一般人看不懂这里的流程,因为使用的是流。

###2. 使用spring中的advice

这种就是spring里面的面向切面编程的使用了,这种方式只能依赖于spring的aspect存在,实现方法为:
(1)声明一个类,比如叫WebRequestAroundAdvice;
(2)定义切入点;
(3)定义切入点的处理方法。
(4)相关代码如下:

WebRequestAroundAdvice.java

  1. package com.zuolin.interceptor;
  2. import java.util.Enumeration;
  3. import javax.servlet.http.HttpServletRequest;
  4. import org.aspectj.lang.ProceedingJoinPoint;
  5. import org.aspectj.lang.annotation.Around;
  6. import org.aspectj.lang.annotation.Aspect;
  7. import org.aspectj.lang.annotation.Pointcut;
  8. import org.slf4j.Logger;
  9. import org.slf4j.LoggerFactory;
  10. import org.springframework.web.context.request.RequestContextHolder;
  11. import org.springframework.web.context.request.ServletRequestAttributes;
  12. import com.alibaba.fastjson.JSONObject;
  13. @Aspect
  14. public class WebRequestAroundAdvice {
  15. private static final Logger logger = LoggerFactory.getLogger(WebRequestAroundAdvice.class);
  16. @Pointcut( value = "execution(* com.zuolin.controller.*.*(..))" )
  17. public void pointcut(){}
  18. @Around("pointcut()")
  19. public Object handle(ProceedingJoinPoint joinPoint) throws Throwable{
  20. preHandle();
  21. Object retVal = joinPoint.proceed();
  22. postHandle(retVal);
  23. return retVal;
  24. }
  25. private void preHandle() {
  26. if (logger.isDebugEnabled()) {
  27. HttpServletRequest request = ( (ServletRequestAttributes) RequestContextHolder.getRequestAttributes() ).getRequest();
  28. StringBuffer sb = new StringBuffer();
  29. sb.append("{");
  30. Enumeration<String> headers = request.getHeaderNames();
  31. int i = 0;
  32. while (headers.hasMoreElements()) {
  33. String header = headers.nextElement();
  34. if (i > 0)
  35. sb.append(", ");
  36. sb.append(header + ": " + request.getHeader(header));
  37. i++;
  38. }
  39. sb.append("}");
  40. logger.debug("Pre handling request: {}, headers: {}", getRequestInfo(request, true), sb.toString());
  41. }
  42. }
  43. private void postHandle(Object retVal) {
  44. if (logger.isDebugEnabled()) {
  45. HttpServletRequest request = ( (ServletRequestAttributes) RequestContextHolder.getRequestAttributes() ).getRequest();
  46. logger.debug("Post handling request: {}, response: {}", getRequestInfo(request, false), JSONObject.toJSONString(retVal));
  47. }
  48. }
  49. private String getRequestInfo(HttpServletRequest request, boolean requestDetails) {
  50. StringBuffer sb = new StringBuffer();
  51. sb.append(request.getMethod()).append(" ");
  52. sb.append(request.getRequestURI());
  53. if (requestDetails) {
  54. Enumeration<String> e = request.getParameterNames();
  55. sb.append(" ").append("{");
  56. int i = 0;
  57. while (e.hasMoreElements()) {
  58. String name = e.nextElement();
  59. String val = request.getParameter(name);
  60. if (val != null && !val.isEmpty()) {
  61. if (i > 0)
  62. sb.append(", ");
  63. sb.append(name).append(": ").append(val);
  64. i++;
  65. }
  66. }
  67. sb.append("}");
  68. }
  69. return sb.toString();
  70. }
  71. }

然后在spring的配置文件中配置这个bean就可以直接使用了。

这种方式的优点就是代码很简洁,只要稍微学过一点AOP都能很容易理解,缺点就是依赖于spring。

这里只使用了spring的around advice,当然,使用其它的几种advice的组合也可以实现相同的效果,或者混合使用拦截器和advice也可以,关键在于你。


欢迎关注我的公众号“彤哥读源码”,查看更多“源码&架构&算法”系列文章, 与彤哥一起畅游源码的海洋。

qrcode

发表评论

表情:
评论列表 (有 0 条评论,791人围观)

还没有评论,来说两句吧...

相关阅读