spring:拦截器(HandlerInterceptor)中获取POST请求参数

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/**");/** 排除的拦截路径(此路径不拦截 */
	}
}
  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

10km

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值