几品飞车|SpringBoot 拦截器获取http请求参数

本文作者:上帝爱吃苹果 本文链接:
获取http请求参数是一种刚需我想有的小伙伴肯定有过获取http请求的需要 , 比如想

  1. 前置获取参数 , 统计请求数据
  2. 做服务的接口签名校验
  3. 敏感接口监控日志
  4. 敏感接口防重复提交
等等各式各样的场景 , 这时你就需要获取 HTTP 请求的参数或者请求body , 一般思路有两种 , 一种就是自定义个AOP去拦截目标方法 , 第二种就是使用拦截器 。 整体比较来说 , 使用拦截器更灵活些 , 因为每个接口的请求参数定义不同 , 使用AOP很难细粒度的获取到变量参数 , 本文主线是采用拦截器来获取HTTP请求 。
定义拦截器获取请求基于 spring-boot-starter-parent 2.1.9.RELEASE
看起来这个很简单 , 这里就直接上code , 定义个拦截器
/** * @author axin * @summary HTTP请求拦截器 */@Slf4jpublic class RequestInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//获取请求参数String queryString = request.getQueryString();log.info("请求参数:{}", queryString);//获取请求bodybyte[] bodyBytes = StreamUtils.copyToByteArray(request.getInputStream());String body = new String(bodyBytes, request.getCharacterEncoding());log.info("请求体:{}", body);return true;}}然后把这个拦截器配置一下中:
/** * WebMVC配置 , 你可以集中在这里配置拦截器、过滤器、静态资源缓存等 */@Configurationpublic class WebMvcConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new RequestInterceptor()).addPathPatterns("/**");}}定义个接口测试一下
/** * @author axin * @summary 提交测试接口 */@Slf4j@RestControllerpublic class MyHTTPController {@GetMapping("/v1/get")public void get(@RequestParam("one") String one,@RequestParam("two") BigDecimal number) {log.info("参数:{},{}", one, number);}@PostMapping("/v1/post")public void check(@RequestBody User user) {log.info("{}", JSON.toJSONString(user));}}GET请求获取请求参数示例:
几品飞车|SpringBoot 拦截器获取http请求参数POST请求获取请求Body示例:
几品飞车|SpringBoot 拦截器获取http请求参数我们发现拦截器在获取HTTP请求的body时出现了 400 (Required request body is missing: public void com.axin.world.controller.MyHTTPController.check(com.axin.world.domain.User));同时也发现拦截器竟然走了两遍 , 这又是咋回事呢?
几品飞车|SpringBoot 拦截器获取http请求参数为什么拦截器会重复调两遍呢?其实是因为 tomcat截取到异常后就转发到/error页面 , 就在这个转发的过程中导致了springmvc重新开始DispatcherServlet的整个流程 , 所以拦截器执行了两次 , 我们可以看下第二次调用时的url路径:
几品飞车|SpringBoot 拦截器获取http请求参数ServletInputStream(CoyoteInputStream) 输入流无法重复调用而之前出现的 Required request body is missing 错误 其实是ServletInputStream被读取后无法第二次再读取了 , 所以我们要把读取过的内容存下来 , 然后需要的时候对外提供可被重复读取的ByteArrayInputStream 。


推荐阅读