当前位置: 首页 > news >正文

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

1‌、HTTP请求流的单次读取特性

‌HTTP请求的输入流(InputStream)设计为只能被读取一次,读取后流指针会移动到末尾,再次读取将返回空数据。

2、获取请求参数为啥需要结合Filter使用

过滤器作为请求处理的第一道关卡(请求顺序:Filter--->Interceptor-->Controller),是唯一能完整获取原始请求体的环节,通过过滤器包装请求,可避免后续流程出现HttpMessageNotReadableException异常(PS:过滤器执行的order需要设置成最高优先顺序

3、技术实现关键点

过滤器通过HttpServletRequestWrapper将原始流数据缓存到内存或字节数组中,后续通过重写getInputStream()返回新的流对象。

HttpServletRequestWrapper:核心作用

  1. 请求数据拦截与修改
    允许在请求到达业务逻辑前拦截并修改请求参数、头信息、请求体等,例如:

    • 参数解密/验签
    • 请求体缓存(实现重复读取)
    • 敏感数据脱敏
  2. 功能增强
    在不破坏原生 HttpServletRequest 的前提下扩展功能,例如:

    • 添加自定义请求属性
    • 重写 getParameter() 实现参数预处理
    • 支持 JSON 格式的请求体直接解析
  3. 设计模式应用
    采用‌装饰器模式‌,避免继承体系的臃肿,灵活组合功能。

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");}
}

相关文章:

  • 【Dify系列教程重置精品版】第七章:在Dify对话中显示本地图片之FastAPI与Uvicorn
  • 新能源行业Profinet转CanOpen转换:跨越通信鸿沟的桥梁
  • NHANES指标推荐:NfL
  • 【数据结构】01Trie
  • el-table中合并表格后横向变高样式无效
  • 国标GB28181软件EasyGBS雪亮工程打造智能高效的视频监控新体系
  • MySQL锁机制详解!
  • RocketMQ的事务消息机制
  • vue 手机端 封装全局使用的提示框 (vant)
  • YOGA Air X ILL10(83CX)/YOGA 14 ILL10X(83LC)2025款恢复开箱状态原装出厂Win11系统OEM镜像
  • SEMI E40-0200 STANDARD FOR PROCESSING MANAGEMENT(加工管理标准)-(三)完结
  • 【LeetCode 42】接雨水(单调栈、DP、双指针)
  • 深入剖析 I/O 复用之 select 机制
  • C#简易Modbus从站仿真器
  • 2025年排名前十进销存软件大测评
  • Coding Practice,48天强训(32)
  • 【嵌入式开发-IIC】
  • OptiStruct动力分析超单元卡片说明(2)
  • 【嵌入式开发-xxxxx】
  • 计算机网络笔记(十五)——3.2点对点协议PPP
  • 日月谭天 | 赖清德倒行逆施“三宗罪”,让岛内民众怒不可遏
  • 国家统计局:1-4月份,全国固定资产投资同比增长4.0%
  • 西域都护府博物馆今日在新疆轮台县开馆
  • 俄乌刚谈完美国便筹划与两国领导人通话,目的几何?
  • 视频丨为救心梗同学缺席职教高考的小伙姜昭鹏完成补考
  • 光速晋级!2025年多哈世乒赛孙颖莎4比0战胜对手