SpringBoot之用拦截器避免重复请求(springboot拦截器不起作用)

  本篇文章为你整理了SpringBoot之用拦截器避免重复请求(springboot拦截器不起作用)的详细内容,包含有springboot拦截器拦截所有请求 springboot拦截器不起作用 springboot拦截器的处理逻辑 springboot拦截器异常处理 SpringBoot之用拦截器避免重复请求,希望能帮助你了解 SpringBoot之用拦截器避免重复请求。

   开发中可能会经常遇到短时间内由于用户的重复点击导致几秒之内重复的请求,可能就是在这几秒之内由于各种问题,比如网络,事务的隔离性等等问题导致了数据的重复等问题,因此在日常开发中必须规避这类的重复请求操作,今天就用拦截器简单的处理一下这个问题。

  
什么是拦截器

  Spring MVC中的拦截器(Interceptor)类似于Servlet中的过滤器(Filter),它主要用于拦截用户请求并作相应的处理。例如通过拦截器可以进行权限验证、记录请求信息的日志、判断用户是否登录等。

  如何自定义拦截器

  自定义一个拦截器非常简单,只需要实现HandlerInterceptor这个接口即可,这个接口有三个可实现的方法

  
preHandle()方法:该方法会在控制器方法前执行,其返回值表示是否知道如何写一个接口。中断后续操作。当其返回值为true时,表示继续向下执行;当其返回值为false时,会中断后续的所有操作(包括调用下一个拦截器和控制器类中的方法执行等)。

  
postHandle()方法:该方法会在控制器方法调用之后,且解析视图之前执行。可以通过此方法对请求域中的模型和视图做出进一步的修改。

  
afterCompletion()方法:该方法会在整个请求完成,即视图渲染结束之后执行。可以通过此方法实现一些资源清理、记录日志信息等工作。

  
如何让拦截器在Spring Boot中生效

  想要在Spring Boot生效其实很简单,只需要定义一个配置类,实现WebMvcConfigurer这个接口,并且实现其中的addInterceptors()方法即可,代码如下:

  

@Configuration

 

  public class WebConfig implements WebMvcConfigurer {

   @Autowired

   private XXX xxx;

   @Override

   public void addInterceptors(InterceptorRegistry registry) {

   // 不拦截的uri

   final String[] commonExclude = {}};

   registry.addInterceptor(xxx).excludePathPatterns(commonExclude);

  

 

  用拦截器规避重复请求

  开发中可能会经常遇到短时间内由于用户的重复点击导致几秒之内重复的请求,可能就是在这几秒之内由于各种问题,比如网络,事务的隔离性等等问题导致了数据的重复等问题,因此在日常开发中必须规避这类的重复请求操作,今天就用拦截器简单的处理一下这个问题。

  在接口执行之前先对指定接口(比如标注某个注解的接口)进行判断,如果在指定的时间内(比如5秒)已经请求过一次了,则返回重复提交的信息给调用者。

  根据什么判断这个接口已经请求了?

  根据项目的架构可能判断的条件也是不同的,比如IP地址,用户唯一标识、请求参数、请求URI等等其中的某一个或者多个的组合。

  这个具体的信息存放在哪?

  由于是短时间内甚至是瞬间并且要保证定时失效,肯定不能存在事务性数据库中了,因此常用的几种数据库中只有Redis比较合适了。

  Docker启动一个Redis

  

docker pull redis:7.0.4

 

  docker run -itd \

   --name redis \

   -p 6379:6379 \

   redis:7.0.4

  

 

  创建一个Spring Boot项目

  使用idea的Spring Initializr来创建一个Spring Boot项目,如下图:

  pom.xml文件如下

  

 ?xml version="1.0" encoding="UTF-8"? 

 

   project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"

   modelVersion 4.0.0 /modelVersion

   parent

   groupId org.springframework.boot /groupId

   artifactId spring-boot-starter-parent /artifactId

   version 2.7.5 /version

   relativePath/ !-- lookup parent from repository --

   /parent

   groupId com.example /groupId

   artifactId springboot_06 /artifactId

   version 0.0.1-SNAPSHOT /version

   name springboot_06 /name

   description Demo project for Spring Boot /description

   properties

   java.version 1.8 /java.version

   /properties

   dependencies

   dependency

   groupId org.springframework.boot /groupId

   artifactId spring-boot-starter-web /artifactId

   /dependency

   dependency

   groupId org.projectlombok /groupId

   artifactId lombok /artifactId

   optional true /optional

   /dependency

   !--spring redis配置--

   dependency

   groupId org.springframework.boot /groupId

   artifactId spring-boot-starter-data-redis /artifactId

   !-- 1.5的版本默认采用的连接池技术是jedis 2.0以上版本默认连接池是lettuce, 在这里采用jedis,所以需要排除lettuce的jar --

   exclusions

   exclusion

   groupId redis.clients /groupId

   artifactId jedis /artifactId

   /exclusion

   exclusion

   groupId io.lettuce /groupId

   artifactId lettuce-core /artifactId

   /exclusion

   /exclusions

   /dependency

   dependency

   groupId redis.clients /groupId

   artifactId jedis /artifactId

   /dependency

   dependency

   groupId org.springframework.boot /groupId

   artifactId spring-boot-starter-test /artifactId

   scope test /scope

   exclusions

   exclusion

   groupId org.junit.vintage /groupId

   artifactId junit-vintage-engine /artifactId

   /exclusion

   /exclusions

   /dependency

   /dependencies

   build

   plugins

   plugin

   groupId org.springframework.boot /groupId

   artifactId spring-boot-maven-plugin /artifactId

   /plugin

   /plugins

   /build

   /project

  

 

  配置Redis

  application.properties

  

spring.redis.host=127.0.0.1

 

  spring.redis.database=1

  spring.redis.port=6379

  

 

  定义一个注解

  

package com.example.springboot_06.intercept;

 

  import java.lang.annotation.ElementType;

  import java.lang.annotation.Retention;

  import java.lang.annotation.RetentionPolicy;

  import java.lang.annotation.Target;

  @Target({ElementType.TYPE, ElementType.METHOD})

  @Retention(RetentionPolicy.RUNTIME)

  public @interface RepeatSubmit {

   * 默认失效时间5秒

   * @return

   long seconds() default 5;

  

 

  创建一个拦截器

  

package com.example.springboot_06.intercept;

 

  import lombok.extern.slf4j.Slf4j;

  import org.springframework.beans.factory.annotation.Autowired;

  import org.springframework.core.annotation.AnnotationUtils;

  import org.springframework.data.redis.core.StringRedisTemplate;

  import org.springframework.stereotype.Component;

  import org.springframework.web.method.HandlerMethod;

  import org.springframework.web.servlet.HandlerInterceptor;

  import javax.servlet.http.HttpServletRequest;

  import javax.servlet.http.HttpServletResponse;

  import java.util.Objects;

  import java.util.concurrent.TimeUnit;

   * 重复请求的拦截器

   * @Component:该注解将其注入到IOC容器中

  @Slf4j

  @Component

  public class RepeatSubmitInterceptor implements HandlerInterceptor {

   * Redis的API

   @Autowired

   private StringRedisTemplate stringRedisTemplate;

   * preHandler方法,在controller方法之前执行

   * p

   * 判断条件仅仅是用了uri,实际开发中根据实际情况组合一个唯一识别的条件。

   @Override

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

   if (handler instanceof HandlerMethod) {

   // 只拦截标注了@RepeatSubmit该注解

   HandlerMethod method = (HandlerMethod) handler;

   // 标注在方法上的@RepeatSubmit

   RepeatSubmit repeatSubmitByMethod = AnnotationUtils.findAnnotation(method.getMethod(), RepeatSubmit.class);

   // 标注在controler类上的@RepeatSubmit

   RepeatSubmit repeatSubmitByCls = AnnotationUtils.findAnnotation(method.getMethod().getDeclaringClass(), RepeatSubmit.class);

   // 没有限制重复提交,直接跳过

   if (Objects.isNull(repeatSubmitByMethod) Objects.isNull(repeatSubmitByCls)) {

   log.info("isNull");

   return true;

   // todo: 组合判断条件,这里仅仅是演示,实际项目中根据架构组合条件

   //请求的URI

   String uri = request.getRequestURI();

   //存在即返回false,不存在即返回true

   Boolean ifAbsent = stringRedisTemplate.opsForValue().setIfAbsent(uri, "",

   Objects.nonNull(repeatSubmitByMethod) ? repeatSubmitByMethod.seconds() : repeatSubmitByCls.seconds(), TimeUnit.SECONDS);

   //如果存在,表示已经请求过了,直接抛出异常,由全局异常进行处理返回指定信息

   if (ifAbsent != null !ifAbsent) {

   String msg = String.format("url:[%s]重复请求", uri);

   log.warn(msg);

   // throw new RepeatSubmitException(msg);

   throw new Exception(msg);

   return true;

  

 

  配置拦截器

  

package com.example.springboot_06.config;

 

  import com.example.springboot_06.intercept.RepeatSubmitInterceptor;

  import org.springframework.beans.factory.annotation.Autowired;

  import org.springframework.context.annotation.Configuration;

  import org.springframework.web.servlet.config.annotation.InterceptorRegistry;

  import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

  @Configuration

  public class WebConfig implements WebMvcConfigurer {

   @Autowired

   private RepeatSubmitInterceptor repeatSubmitInterceptor;

   @Override

   public void addInterceptors(InterceptorRegistry registry) {

   // 不拦截的uri

   final String[] commonExclude = {"/error", "/files/**"};

   registry.addInterceptor(repeatSubmitInterceptor).excludePathPatterns(commonExclude);

  

 

  写个测试Controller

  

package com.example.springboot_06.controller;

 

  import com.example.springboot_06.intercept.RepeatSubmit;

  import lombok.extern.slf4j.Slf4j;

  import org.springframework.http.ResponseEntity;

  import org.springframework.web.bind.annotation.RequestMapping;

  import org.springframework.web.bind.annotation.RestController;

   * 标注了@RepeatSubmit注解,全部的接口都需要拦截

  @Slf4j

  @RestController

  @RequestMapping("/user")

  @RepeatSubmit

  public class UserController {

   @RequestMapping("/save")

   public ResponseEntity save() {

   log.info("/user/save");

   return ResponseEntity.ok("save success");

  

 

  以上就是SpringBoot之用拦截器避免重复请求(springboot拦截器不起作用)的详细内容,想要了解更多 SpringBoot之用拦截器避免重复请求的内容,请持续关注盛行IT软件开发工作室。

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: