实现接口防抖

接口防抖

接口防抖主要用于控制在短时间内多次触发的事件或请求,确保在一定时间内只执行一次操作。这种技术可以应用于前端和后端,以优化性能和用户体验,减少不必要的计算或网络请求。

虽然说接口请求可以直接在前端实现,但是为了防止有用户恶意绕过前端限制,直接通过工具或脚本来发起请求。本着不相信任何人的原则,还是在后端也做防抖合适。

在这里插入图片描述

redis实现

思路

实现一个拦截器,在请求到达控制器之前拦截。然后判断redis中有没有数据,如果有 说明指定时间内已经调用过了,拦截,如果没有,说明是正常请求,在redis中添加一条数据并设置过期时间,表示指定时间内不能再请求了

@Component
public class RepeatSubmitInterceptor implements HandlerInterceptor {

   @Autowired
   private StringRedisTemplate redisTemplate;

   private static final long DEBOUNCE_TIME = 20; // 防抖时间(秒)

   @Override
   public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
       String key = "repeat-submit:" + request.getSession().getId() + ":" + request.getRequestURI();

       if (Boolean.TRUE.equals(redisTemplate.hasKey(key))) {
           response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
           return false; // 拦截请求
       }

       // 设置防抖时间 (秒)
       redisTemplate.opsForValue().set(key, "1", DEBOUNCE_TIME, TimeUnit.SECONDS);
       return true; // 继续执行请求
   }
}

注册拦截器

@Configuration
public class WebConfig implements WebMvcConfigurer {

   @Autowired
   private RepeatSubmitInterceptor repeatSubmitInterceptor;

   @Override
   public void addInterceptors(InterceptorRegistry registry) {
       // 添加拦截器
       registry.addInterceptor(repeatSubmitInterceptor).addPathPatterns("/**");
   }
}

addPathPatterns(“/**”) 表示拦截所有的方法,若需要拦截指定的方法增加addPathPatterns(“方法路径”)即可

注解+AOP实现

通过在方法上增加注解的方式,实现指定接口的防抖

思路‌

通过AOP的前置通知来拦截标记了RepeatSubmit注解的方法。‌

在方法执行前,检查Redis中是否存在锁。如果存在锁,则返回提示信息。如果不存在锁,则在Redis中设置锁,并继续执行方法。

定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RepeatSubmit {
    /* 防抖时间 */
    int time() default 1000; // 默认间隔时间,单位毫秒
}
切面
@Aspect
@Component
public class RepeatSubmitAspect {
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    /* 只增强带RepeatSubmit注解的方法 */
    @Before("@annotation(com.example.demo.antishake.annotation.RepeatSubmit)")
    public void interceptor(JoinPoint joinPoint) throws Throwable {
        // 获取HttpServletRequest对象
        HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
        // 获取方法签名
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        // 获取方法
        Method method = methodSignature.getMethod();
        // 获取方法上的注解
        RepeatSubmit annotation = method.getAnnotation(RepeatSubmit.class);
        // 用调用方法的路径+用户的token作为key
        String key = "repeat-submit:"+ request.getRequestURI()+request.getHeader("Authorization");
        // 判断缓存中是否存在该key, 存在则说明已经提交过该请求
        if (Boolean.TRUE.equals(stringRedisTemplate.hasKey(key))) {
            throw new RuntimeException("请勿重复提交");
        }
        if (annotation != null) {
            // 获取注解参数
            int time = annotation.time();
            stringRedisTemplate.opsForValue().set(key, "1", time, TimeUnit.MILLISECONDS);
        }
    }
}
测试
@RestController
public class TestController {

    @RepeatSubmit(time = 10000)
    @GetMapping("/test")
    public String test(){
        return "请求成功";
    }

    @GetMapping("/test01")
    public String test01(){
        return "请求成功01";
    }
}

测试test接口,防抖时间是10秒,连续请求就会失败

注意:上面当作锁的key,可能不太合理,根据具体场景修改,如提交的参数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值