spring的拦截器(HandlerInterceptor)机制工作在收到HTTP请求之后,参数解析(反序列化)之前阶段。
这个阶段,因为请求方法的参数还没有被反序列化,所以要想获取HTTP的请求参数,就要自己从HttpServletRequest中获取。
对于GET方法,这个很简单,调用HttpServletRequest.getParameter就可以得到。
对于POST方法要从请求内容里获取,即从HttpServletRequest里的InputStream里获取到,然后解析为JSON就可以了。
  但是,InputStream是单向的,一次性的,如果在拦截器中直接调用HttpServletRequest.getInputStream()获取了数据,后续方法调用解析参数时再调用HttpServletRequest.getInputStream()读取就会失败,因为输入流结束了。
  所以拦截器(HandlerInterceptor)中获取POST请求参数本身并不是问题,问题就是要解决HttpServletRequest的输入流能被多次读取问题。
  解决的思路就是将HttpServletRequest基于HttpServletRequestWrapper封装起来,实现javax.servlet.Filter接口,用封装后的HttpServletRequestWrapper替换掉整个请求链路中的HTTP请求实例(HttpServletRequest)。
  要实现的HttpServletRequestWrapper类负责将HttpServletRequest中的InputStream完整数据读取出来保存下来,以便重复读取。
  这样才能确保HttpServletRequest中的InputStream可以多次被读取。
  
以下为HTTP请求过滤器过滤器(javax.servlet.Filter)的实现,包含了HttpServletRequestWrapper封装类实现

BufferedRequestFilter.java

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ReadListener;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

import org.springframework.stereotype.Component;
import com.google.common.base.Charsets;

/**
 * HTTP请求过滤器<br>
 * 将{@link HttpServletRequest}封装为{@link BufferedRequestWrapper}替换掉整个请求链路中的Request。
 * 
 * @author guyadong
 *
 */
@Component
@WebFilter(urlPatterns = "/*", filterName = "bufferedRequestFilter")
public class BufferedRequestFilter implements Filter {

	@Override
	public void init(FilterConfig filterConfig) throws ServletException {

	}

	@Override
	public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
			throws IOException, ServletException {
		if (servletRequest instanceof HttpServletRequest && !(servletRequest instanceof BufferedRequestWrapper)) {
			/** 将请求封装为BufferedRequestWrapper传递下去 */
			HttpServletRequestWrapper requestWrapper = new BufferedRequestWrapper((HttpServletRequest) servletRequest);
			filterChain.doFilter(requestWrapper, servletResponse);
		} else {
			filterChain.doFilter(servletRequest, servletResponse);
		}
	}

	@Override
	public void destroy() {

	}

	/**
	 * 基于 HttpServletRequestWrapper封装,允许 HTTP请求中的{@link InputStream}被重复读取
	 */
	private static class BufferedRequestWrapper extends HttpServletRequestWrapper {
		private final byte[] buffer;

		public BufferedRequestWrapper(HttpServletRequest request) throws IOException {
			super(request);
			/** 读取InputSteam中的数据保存到 buffer */
			InputStream is = request.getInputStream();
			ByteArrayOutputStream os = new ByteArrayOutputStream();
			byte[] buf = new byte[1024];
			int readCount;
			while ((readCount = is.read(buf)) > 0) {
				os.write(buf, 0, readCount);
			}
			buffer = os.toByteArray();
		}

		@Override
		public ServletInputStream getInputStream() {
			return new BufferedServletInputStream(buffer);
		}

		@Override
		public BufferedReader getReader() throws IOException {
			return new BufferedReader(new InputStreamReader(this.getInputStream(), Charsets.UTF_8));
		}

	}

	private static class BufferedServletInputStream extends ServletInputStream {
		private final ByteArrayInputStream inputStream;

		public BufferedServletInputStream(byte[] buffer) {
			this.inputStream = new ByteArrayInputStream(buffer);
		}

		@Override
		public int read() throws IOException {
			return inputStream.read();
		}

		@Override
		public boolean isFinished() {
			return inputStream.available() == 0;
		}

		@Override
		public boolean isReady() {
			return !isFinished();
		}

		@Override
		public void setReadListener(ReadListener readListener) {
			// Not implemented
		}
	}
}

有了上面WapperRequestFilter的支持,HandlerInterceptor中就可以正常读HttpServletRequest的InputStream,而不影响后续的参数反序列化了。
拦截器读取HTTP 请求参数示例:

import java.lang.reflect.Method;
import java.util.Set;

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

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

/**
 * HTTP请求拦截器,实现在HTTP请求解析之前获取POST请求中的参数
 */
@Configuration
public class TokenExtractInterceptor implements HandlerInterceptor,WebMvcConfigurer{
	public TokenExtractInterceptor() {
	}
	
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		JSONObject body = JSON.parseObject(request.getInputStream(),JSONObject.class);
		Systemm.out.printf("body:%s\n",JSON.toJSONString(body, true));
		return true;
	}
	@Override
	public void addInterceptors(InterceptorRegistry registry) {
		registry.addInterceptor(this)
				.addPathPatterns("/**")  /** 添加拦截路径 */
				.excludePathPatterns("/error","/swagger-resources/**","/swagger/**", "/swagger2/**");/** 排除的拦截路径(此路径不拦截 */
	}
}
Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐