SpringBoot拦截器如何获取http请求参数

 更新时间:2020年09月10日 09:20:18   作者:上帝爱吃苹果-Soochow  
这篇文章主要给大家介绍了SpringBoot拦截器如何获取http请求参数的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

1.1、获取http请求参数是一种刚需

我想有的小伙伴肯定有过获取http请求的需要,比如想

  1. 前置获取参数,统计请求数据
  2. 做服务的接口签名校验
  3. 敏感接口监控日志
  4. 敏感接口防重复提交

等等各式各样的场景,这时你就需要获取 HTTP 请求的参数或者请求body,一般思路有两种,一种就是自定义个AOP去拦截目标方法,第二种就是使用拦截器。整体比较来说,使用拦截器更灵活些,因为每个接口的请求参数定义不同,使用AOP很难细粒度的获取到变量参数,本文主线是采用拦截器来获取HTTP请求。

1.2、定义拦截器获取请求

基于 spring-boot-starter-parent 2.1.9.RELEASE

看起来这个很简单,这里就直接上code,定义个拦截器

/**
 * @author axin
 * @summary HTTP请求拦截器
 */
@Slf4j
public class RequestInterceptor implements HandlerInterceptor {

  @Override
  public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

    //获取请求参数
    String queryString = request.getQueryString();
    log.info("请求参数:{}", queryString);

    //获取请求body
    byte[] bodyBytes = StreamUtils.copyToByteArray(request.getInputStream());
    String body = new String(bodyBytes, request.getCharacterEncoding());

    log.info("请求体:{}", body);
    return true;
  }
}

然后把这个拦截器配置一下中:

/**
 * WebMVC配置,你可以集中在这里配置拦截器、过滤器、静态资源缓存等
 */
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

  @Override
  public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(new RequestInterceptor()).addPathPatterns("/**");
  }
}

定义个接口测试一下

/**
 * @author axin
 * @summary 提交测试接口
 */
@Slf4j
@RestController
public 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请求获取请求参数示例:

POST请求获取请求Body示例:

我们发现拦截器在获取HTTP请求的body时出现了 400 (Required request body is missing: public void com.axin.world.controller.MyHTTPController.check(com.axin.world.domain.User));同时也发现拦截器竟然走了两遍,这又是咋回事呢?

1.3、为什么拦截器会重复调两遍呢?

其实是因为 tomcat截取到异常后就转发到/error页面,就在这个转发的过程中导致了springmvc重新开始DispatcherServlet的整个流程,所以拦截器执行了两次,我们可以看下第二次调用时的url路径:

1.4、ServletInputStream(CoyoteInputStream) 输入流无法重复调用

而之前出现的 Required request body is missing 错误 其实是ServletInputStream被读取后无法第二次再读取了,所以我们要把读取过的内容存下来,然后需要的时候对外提供可被重复读取的ByteArrayInputStream。

对于MVC的过滤器来说,我们就需要重写 ServletInputStream 的 getInputStream()方法。

1.5、自定义 HttpServletRequestWrapper

为了 重写 ServletInputStream 的 getInputStream()方法,我们需要自定义一个 HttpServletRequestWrapper :

/**
* @author Axin
* @summary 自定义 HttpServletRequestWrapper 来包装输入流
*/
public class AxinHttpServletRequestWrapper extends HttpServletRequestWrapper {

  /**
   * 缓存下来的HTTP body
   */
  private byte[] body;

  public AxinHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
    super(request);
    body = StreamUtils.copyToByteArray(request.getInputStream());
  }

  /**
   * 重新包装输入流
   * @return
   * @throws IOException
   */
  @Override
  public ServletInputStream getInputStream() throws IOException {
    InputStream bodyStream = new ByteArrayInputStream(body);
    return new ServletInputStream() {

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

      /**
       * 下面的方法一般情况下不会被使用,如果你引入了一些需要使用ServletInputStream的外部组件,可以重点关注一下。
       * @return
       */
      @Override
      public boolean isFinished() {
        return false;
      }

      @Override
      public boolean isReady() {
        return true;
      }

      @Override
      public void setReadListener(ReadListener readListener) {

      }
    };
  }
  
  @Override
  public BufferedReader getReader() throws IOException {
    InputStream bodyStream = new ByteArrayInputStream(body);
    return new BufferedReader(new InputStreamReader(getInputStream()));
  }
}

然后定义一个 DispatcherServlet子类来分派 上面自定义的 AxinHttpServletRequestWrapper :

/**
* @author Axin
* @summary 自定义 DispatcherServlet 来分派 AxinHttpServletRequestWrapper
*/
public class AxinDispatcherServlet extends DispatcherServlet {

  /**
   * 包装成我们自定义的request
   * @param request
   * @param response
   * @throws Exception
   */
  @Override
  protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    super.doDispatch(new AxinHttpServletRequestWrapper(request), response);
  }
}

然后配置一下:

/**
 * WebMVC配置,你可以集中在这里配置拦截器、过滤器、静态资源缓存等
 */
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

  @Override
  public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(new RequestInterceptor()).addPathPatterns("/**");
  }

  @Bean
  @Qualifier(DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
  public DispatcherServlet dispatcherServlet() {
    return new AxinDispatcherServlet();
  }
}

再调用一下 POST请求:

请求成功!

1.5、总结一下 展望一下

如果你想对HTTP请求做些骚操作,那么前置获取HTTP请求参数是前提,为此文本给出了使用MVC拦截器获取参数的样例。

在获取HTTP Body 的时候,出现了 Required request body is missing 的错误,同时拦截器还出现执行了两遍的问题,这是因为 ServletInputStream被读取了两遍导致的,tomcat截取到异常后就转发到 /error 页面 被拦截器拦截到了,拦截器也就执行了两遍。

为此我们通过自定义 HttpServletRequestWrapper 来包装一个可被重读读取的输入流,来达到期望的拦截效果。

在获取到HTTP的请求参数后,我们可以前置做很多操作,比如常用的服务端接口签名验证,敏感接口防重复请求等等。

个人水平有限,如果文章有逻辑错误或表述问题还请指出,欢迎一起交流。

到此这篇关于SpringBoot拦截器如何获取http请求参数的文章就介绍到这了,更多相关SpringBoot拦截器获取http请求参数内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java中的锁分类的详细介绍

    Java中的锁分类的详细介绍

    这篇文章主要介绍了Java中的锁分类的详细介绍,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-09-09
  • SpringBoot集成RocketMQ实现消息发送的三种方式

    SpringBoot集成RocketMQ实现消息发送的三种方式

    RocketMQ 支持3 种消息发送方式: 同步 (sync)、异步(async)、单向(oneway),本文就将给大家介绍一下SpringBoot集成RocketMQ实现消息发送的三种方式文中有详细的代码示例,需要的朋友可以参考下
    2023-09-09
  • Java同学找工作最懵圈的问题:到底啥是分布式系统开发经验?(推荐)

    Java同学找工作最懵圈的问题:到底啥是分布式系统开发经验?(推荐)

    这篇文章主要介绍了分布式系统开发经验,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-04-04
  • SpringBoot中获取profile的方法详解

    SpringBoot中获取profile的方法详解

    这篇文章主要介绍了springboot获取profile的操作,文中的示例代码讲解详细,具有很好的参考价值,希望对大家有所帮助
    2022-04-04
  • spring boot linux启动方式详解

    spring boot linux启动方式详解

    这篇文章主要介绍了spring boot linux启动方式详解,分为为前台启动,后台启动和脚本启动的各种方式讲解,需要的朋友可以参考下
    2017-11-11
  • java高并发情况下高效的随机数生成器

    java高并发情况下高效的随机数生成器

    这篇文章主要介绍了java高并发情况下高效的随机数生成器,对于性能有要求的同学,可以参考下
    2021-04-04
  • 教你快速学会JPA中所有findBy语法规则

    教你快速学会JPA中所有findBy语法规则

    这篇文章主要介绍了教你快速学会JPA中所有findBy语法规则,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-11-11
  • SpringCloud配置客户端ConfigClient接入服务端

    SpringCloud配置客户端ConfigClient接入服务端

    这篇文章主要为大家介绍了SpringCloud配置客户端ConfigClient接入服务端,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-08-08
  • 使用dom4j解析xml文件问题

    使用dom4j解析xml文件问题

    这篇文章主要介绍了使用dom4j解析xml文件问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-09-09
  • Java读取图片EXIF信息的方法

    Java读取图片EXIF信息的方法

    这篇文章主要介绍了Java读取图片EXIF信息的方法,较为详细的分析了图片EXIF信息的概念、功能及java读取EXIF信息的实现技巧,需要的朋友可以参考下
    2015-07-07

最新评论