前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >重复提交,你是如何处理的?

重复提交,你是如何处理的?

原创
作者头像
Java旅途
修改2020-07-01 10:29:23
1K0
修改2020-07-01 10:29:23
举报
文章被收录于专栏:Java旅途Java旅途

今天早上,新来的同事小王突然问我:“周哥,什么是幂等性啊?”。然后我就跟他解释了一番,幂等性就是说无论你执行几次请求,其结果是一样的。说到了幂等就不得不说重复提交了,你连续点击提交按钮,理论上来说这是同一条数据,数据库应该只能存入一条,而实际上存放了多条,这就违反了幂等性。因此我们就需要做一些处理,来保证连续点击提交按钮后,数据库只能存入一条数据。

防止重复提交的方式很多,这里我就说一下我认为比较好用的一种。

自定义注解+Aop实现

我们通过获取用户ip及访问的接口来判断他是否重复提交,假如这个ip在一段时间内容多次访问这个接口,我们则认为是重复提交,我们将重复提交的请求直接处理即可,不让访问目标接口。

自定义注解

@Target({ElementType.METHOD})

@Retention(RetentionPolicy.RUNTIME)

@Documented

public @interface NoRepeatSubmit {



    /\*\*

     \* 默认1s钟以内算重复提交

     \* @return

     \*/

    long timeout() default 1;

}

Aop处理逻辑

我们将ip+接口地址作为key,随机生成UUID作为value,存入redis。每次请求进来,根据key查询redis,如果存在则说明是重复提交,抛出异常,如果不存在,则是正常提交,将key存入redis。

@Aspect

@Component

public class NoRepeatSubmitAop {



    @Autowired

    private RedisService redisUtils;



    /\*\*

     \*     定义切入点

     \*/

    @Pointcut("@annotation(NoRepeatSubmit)")

    public void noRepeat() {}



    /\*\*

     \*     前置通知:在连接点之前执行的通知

     \* @param point

     \* @throws Throwable

     \*/

    @Before("noRepeat()")

    public void before(JoinPoint point) throws Exception{

        // 接收到请求,记录请求内容

        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();

        HttpServletRequest request = attributes.getRequest();

        Assert.notNull(request, "request can not null");



        // 此处可以用token或者JSessionId

        String token = IpUtils.getIpAddr(request);

        String path = request.getServletPath();

        String key = getKey(token, path);

        String clientId = getClientId();

        List<Object> lGet = redisUtils.lGet(key, 0, -1);

        // 获取注解

        MethodSignature signature = (MethodSignature) point.getSignature();

        Method method = signature.getMethod();

        NoRepeatSubmit annotation = method.getAnnotation(NoRepeatSubmit.class);

        long timeout = annotation.timeout();

        boolean isSuccess = false;

        if (lGet.size()==0 || lGet == null) {

            isSuccess = redisUtils.lSet(key, clientId, timeout);

        }

        if (!isSuccess) {

            // 获取锁失败,认为是重复提交的请求

            redisUtils.lSet(key, clientId, timeout);

            throw new Exception("不可以重复提交");

        }



    }



    private String getKey(String token, String path) {

        return token + path;

    }



    private String getClientId() {

        return UUID.randomUUID().toString();

    }

}

提供接口用来测试

在接口上添加上我们自定义的注解@NoRepeatSubmit

@RequestMapping("/test")

@NoRepeatSubmit

public String tt(HttpServletRequest request) {



    return "1";

}

测试

我们在浏览器中连续请求两次接口。发现第一次接口响应正常内容:1,第二次接口响应了不可重复提交的异常信息。1s之后再点击接口,发现又响应了正常内容。

至此,这种防止重复提交的方式就介绍完了,这样我们就完美防止了接口重复提交。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 自定义注解+Aop实现
    • 自定义注解
      • Aop处理逻辑
        • 提供接口用来测试
          • 测试
          相关产品与服务
          云数据库 Redis
          腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档