拦截器(HandlerInterceptor)中获取请求参数
1、HTTP请求流的单次读取特性
HTTP请求的输入流(InputStream
)设计为只能被读取一次,读取后流指针会移动到末尾,再次读取将返回空数据。
2、获取请求参数为啥需要结合Filter使用
过滤器作为请求处理的第一道关卡(请求顺序:Filter--->Interceptor-->Controller),是唯一能完整获取原始请求体的环节,通过过滤器包装请求,可避免后续流程出现HttpMessageNotReadableException
异常(PS:过滤器执行的order需要设置成最高优先顺序)
3、技术实现关键点
过滤器通过HttpServletRequestWrapper将原始流数据缓存到内存或字节数组中,后续通过重写getInputStream()返回新的流对象。
HttpServletRequestWrapper:核心作用
请求数据拦截与修改
允许在请求到达业务逻辑前拦截并修改请求参数、头信息、请求体等,例如:
- 参数解密/验签
- 请求体缓存(实现重复读取)
- 敏感数据脱敏
功能增强
在不破坏原生HttpServletRequest
的前提下扩展功能,例如:
- 添加自定义请求属性
- 重写
getParameter()
实现参数预处理- 支持 JSON 格式的请求体直接解析
设计模式应用
采用装饰器模式,避免继承体系的臃肿,灵活组合功能。
4、代码具体实现
-
创建自定义wrapper继承HttpServletRequestWrapper
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;import static org.apache.commons.io.IOUtils.toByteArray;public class ParameterRequestWrapper extends HttpServletRequestWrapper {private final byte[] body;public byte[] getBody() {return this.body;}public ParameterRequestWrapper(HttpServletRequest request) throws IOException {super(request);//读取原始流ServletInputStream inputStream = request.getInputStream();//缓存到内存中body = toByteArray(inputStream);}@Overridepublic BufferedReader getReader() {return new BufferedReader(new InputStreamReader(getInputStream()));}/*** 请求体重复获取* @author : zzc* @date : 2025/5/8 14:12*/@Overridepublic ServletInputStream getInputStream() {final ByteArrayInputStream bais = new ByteArrayInputStream(body);return new ServletInputStream() {@Overridepublic boolean isFinished() {return bais.available() == 0;}@Overridepublic boolean isReady() {return true;}@Overridepublic void setReadListener(ReadListener readListener) {throw new UnsupportedOperationException();}@Overridepublic int read() {return bais.read();}};}
}
IOUtils.toByteArray()
方法底层通过ByteArrayOutputStream
实现,会将流数据完整读取并存储在堆内存的字节数组中
-
创建请求体包装过滤器。(执行顺序最高有先级)
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;/*** 请求体包装过滤器* @author : zzc* @date : 2025/5/8 14:15*/
@WebFilter(urlPatterns ={"/demo/add1","/demo/add2"}, filterName = "cache")
@Order(Ordered.HIGHEST_PRECEDENCE) //最高优先执行顺序
public class CacheRequestFilter implements Filter {@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {if (request instanceof HttpServletRequest) {chain.doFilter(new ParameterRequestWrapper((HttpServletRequest) request),response);}}
}
-
创建拦截器
import com.alibaba.fastjson.JSON;
import com.demo.common.filter.ParameterRequestWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.Map;/*** 请求日志记录* @author zzc*/
@Component
public class ParamsInterceptor extends HandlerInterceptorAdapter {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {String method = request.getMethod();if ("GET".equals(method)){// 获取GET/Form参数String param = request.getParameter("param");Map<String, String[]> params = request.getParameterMap();String queryString = "";String[] values = null;for (Map.Entry<String, String[]> entry : params.entrySet()) {values = (String[]) entry.getValue();for (int i = 0; i < values.length; i++) {String value = values[i];System.out.println("参数名称:"+ entry.getKey());System.out.println("参数值:"+ value);}}}else {// 获取JSON body(需配合CacheRequestFilter使用)if (request instanceof ParameterRequestWrapper) {try {String jsonBody = new String(((ParameterRequestWrapper) request).getBody(),StandardCharsets.UTF_8);// 处理JSON数据...Map<String, Object> map = JSON.parseObject(jsonBody, Map.class);} catch (Exception e) {throw new RuntimeException(e);}}}}
}
-
注册拦截器
@Configuration
public class WebAppConfigurer extends WebMvcConfigurerAdapter {@Autowiredpublic ParamsInterceptor paramsInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {// 多个拦截器组成一个拦截器链// addPathPatterns 用于添加拦截规则// excludePathPatterns 用户排除拦截registry.addInterceptor(paramsInterceptor).addPathPatterns("/demo/add1","/demo/add2");}
}