拦截请求返回值的方法

  • Post author:
  • Post category:其他


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

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

这种方法是目前比较通用的方法,不受限于框架限制,不管是用SSH还是springmvc都可以采用,实现方式大致如下:

(1)定义一个拦截器,比如WebRequestInterceptor,在preHandle里面拦截request,从中取出参数等信息,然后打印出来即可。

(2)定义一个过滤器,比如WebResponseFilter,在doFilter里面使用HttpServletResponseWrapper包装传进来的response,然后重写一些方法即可实现。

(注意:这里有个坑,就是直接使用拦截器无法从response里面取出返回值!)

(3)相关代码大致如下:

WebInterceptor.java

package com.zuolin.interceptor;

import java.util.Enumeration;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import com.zuolin.filter.WrapperResponse;

public class WebInterceptor implements HandlerInterceptor {

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

	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object object) throws Exception {
		if (logger.isDebugEnabled()) {

			StringBuffer sb = new StringBuffer();
			sb.append("{");

			Enumeration<String> headers = request.getHeaderNames();
			int i = 0;
			while (headers.hasMoreElements()) {
				String header = headers.nextElement();

				if (i > 0)
					sb.append(", ");
				sb.append(header + ": " + request.getHeader(header));
				i++;
			}
			sb.append("}");

			logger.debug("Pre handling request: {}, headers: {}", getRequestInfo(request, true), sb.toString());
		}
		
		return true;
	}

	private static String getRequestInfo(HttpServletRequest request, boolean requestDetails) {
		StringBuffer sb = new StringBuffer();
		sb.append(request.getMethod()).append(" ");
		sb.append(request.getRequestURI());
		if (requestDetails) {
			Enumeration<String> e = request.getParameterNames();
			sb.append("{");
			int i = 0;
			while (e.hasMoreElements()) {
				String name = e.nextElement();
				String val = request.getParameter(name);

				if (val != null && !val.isEmpty()) {
					if (i > 0)
						sb.append(", ");
					sb.append(name).append(": ").append(val);

					i++;
				}
			}
			sb.append("}");
		}

		return sb.toString();
	}

	@Override
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object object,
			ModelAndView modelAndView) throws Exception {
	}

	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object object,
			Exception exception) throws Exception {
		String requestInfo = getRequestInfo(request, false);
		if (logger.isDebugEnabled()) {
			logger.debug("Complete request: {}", requestInfo);
		}
	}
}

WrapperOutputStream.java

package com.zuolin.filter;

import java.io.ByteArrayOutputStream;
import java.io.IOException;

import javax.servlet.ServletOutputStream;

public class WrapperOutputStream extends ServletOutputStream{
	private ByteArrayOutputStream bos;
	
	public WrapperOutputStream(ByteArrayOutputStream bos) {
		this.bos = bos;
	}
	
	@Override
	public void write(int b) throws IOException {
		bos.write(b);
	}
}

WrapperResponse.java

package com.zuolin.filter;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;

public class WrapperResponse extends HttpServletResponseWrapper{
	private ByteArrayOutputStream buffer;
	private ServletOutputStream out;
	
	public WrapperResponse(HttpServletResponse response) throws UnsupportedEncodingException {
		super(response);
		buffer = new ByteArrayOutputStream();
		out = new WrapperOutputStream(buffer);
	}

	@Override
	public ServletOutputStream getOutputStream() throws IOException {
		return out;
	}
	
	@Override
	public void flushBuffer() throws IOException {
		if (out != null) {
			out.flush();
		}
	}
	
	public byte[] getContent() throws IOException {
		flushBuffer();
		return buffer.toByteArray();
	}
}

ResponseFilter.java

package com.zuolin.filter;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ResponseFilter implements Filter {
	private static final Logger logger = LoggerFactory.getLogger(ResponseFilter.class);
	
	@Override
	public void destroy() {
	}

	@Override
	public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain filterChain) throws IOException, ServletException {
		WrapperResponse wrapperResponse = new WrapperResponse((HttpServletResponse) response);
		filterChain.doFilter(request, wrapperResponse);
		byte[] content = wrapperResponse.getContent();
		if (logger.isDebugEnabled() && content != null && content.length > 0) {
			logger.debug("Response Content: {}", new String(content));
		}
		ServletOutputStream out = response.getOutputStream();
		out.write(content);
		out.flush();
	}

	@Override
	public void init(FilterConfig paramFilterConfig) throws ServletException {
	}

}

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

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

###2. 使用spring中的advice

这种就是spring里面的面向切面编程的使用了,这种方式只能依赖于spring的aspect存在,实现方法为:

(1)声明一个类,比如叫WebRequestAroundAdvice;

(2)定义切入点;

(3)定义切入点的处理方法。

(4)相关代码如下:

WebRequestAroundAdvice.java

package com.zuolin.interceptor;

import java.util.Enumeration;

import javax.servlet.http.HttpServletRequest;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.alibaba.fastjson.JSONObject;

@Aspect
public class WebRequestAroundAdvice {
	private static final Logger logger = LoggerFactory.getLogger(WebRequestAroundAdvice.class);
	
	@Pointcut( value = "execution(* com.zuolin.controller.*.*(..))" )
	public void pointcut(){}
	
	@Around("pointcut()")
	public Object handle(ProceedingJoinPoint joinPoint) throws Throwable{
		preHandle();
		
		Object retVal = joinPoint.proceed();
		
		postHandle(retVal);
		
		return retVal;
	}
	
	
	private void preHandle() {
		if (logger.isDebugEnabled()) {
			HttpServletRequest request = ( (ServletRequestAttributes) RequestContextHolder.getRequestAttributes() ).getRequest();

			StringBuffer sb = new StringBuffer();
			sb.append("{");

			Enumeration<String> headers = request.getHeaderNames();
			int i = 0;
			while (headers.hasMoreElements()) {
				String header = headers.nextElement();

				if (i > 0)
					sb.append(", ");
				sb.append(header + ": " + request.getHeader(header));
				i++;
			}
			sb.append("}");

			logger.debug("Pre handling request: {}, headers: {}", getRequestInfo(request, true), sb.toString());
		}
	}

	private void postHandle(Object retVal) {
		if (logger.isDebugEnabled()) {
			HttpServletRequest request = ( (ServletRequestAttributes) RequestContextHolder.getRequestAttributes() ).getRequest();
			logger.debug("Post handling request: {}, response: {}", getRequestInfo(request, false), JSONObject.toJSONString(retVal));
		}
	}

	private String getRequestInfo(HttpServletRequest request, boolean requestDetails) {
		StringBuffer sb = new StringBuffer();
		sb.append(request.getMethod()).append(" ");
		sb.append(request.getRequestURI());
		if (requestDetails) {
			Enumeration<String> e = request.getParameterNames();
			sb.append(" ").append("{");
			int i = 0;
			while (e.hasMoreElements()) {
				String name = e.nextElement();
				String val = request.getParameter(name);

				if (val != null && !val.isEmpty()) {
					if (i > 0)
						sb.append(", ");
					sb.append(name).append(": ").append(val);

					i++;
				}
			}
			sb.append("}");
		}

		return sb.toString();
	}
}

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

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

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


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

qrcode



版权声明:本文为tangtong1原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。