5

如何防止接口重复提交?

 1 year ago
source link: https://www.51cto.com/article/722500.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

如何防止接口重复提交?

作者:鸭血粉丝Tang 2022-11-11 07:34:43
对于下单流量不算高的系统,可以采用这种请求唯一ID+数据表增加唯一索引约束的方式,来防止接口重复提交!

对于投入运营的软件系统,最近小编在巡检项目数据库的时候,发现某些表存在不少的重复数据,对于这样的脏数据,初步分析大致的来源有以下可能:

1.由于用户误操作,多次点击表单提交按钮

2.由于网速等原因造成页面卡顿,用户重复刷新提交页面

3.黑客或恶意用户使用 postman 等网络工具,重复恶意提交表单

这些情况都可能会导致表单重复提交,造成数据重复,比如订单表,重复提交订单数据所造成的问题,可能不仅仅是数据上的混乱,也会造成业务混乱。

那么问题来了,我们该如何防止用户重复提交数据呢?

方案实践如下!

二、方案实践

下面我们以防止重复提交订单为例,向大家介绍最简单的、成本最低的解决办法。

我们先来看一张图,这张图就是本次方案的核心流程图。

图片

实现的逻辑,流程如下:

1.当用户进入订单提交界面的时候,调用后端获取请求唯一ID,并将唯一ID值埋点在页面里面

2.当用户点击提交按钮时,后端检查这个唯一ID是否用过,如果没有用过,继续后续逻辑;如果用过,就提示重复提交

3.最关键的一步操作,就是把这个唯一ID 存入业务表中,同时设置这个字段为唯一索引类型,从数据库层面做防止重复提交

防止重复提交的大体思路如上,实践代码如下!

2.1、给数据库表增加唯一键约束

以订单表为例,新增一个request_id字段,并设置为唯一约束,结构如下:

CREATE TABLE tb_order (
  id bigint(20) unsigned NOT NULL,
  order_no varchar(100) NOT NULL,
  ....
  request_id varchar(36) NOT NULL,
  PRIMARY KEY (id),
  UNIQUE KEY uniq_request_id (request_id) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

2.2、编写获取请求唯一ID的接口

@RestController
@RequestMapping("api")
public class CommonController {

    /**
     * 获取getRequestId
     * @return
     */
    @RequestMapping("getRequestId")
    public ResResult getRequestId(){
        String uuid = UUID.randomUUID().toString();
        return ResResult.getSuccess(uuid);
    }
}

2.3、业务提交的时候,检查唯一ID

@RestController
@RequestMapping("order")
public class OrderController {

    @Autowired
    private OrderService orderService;

    /**
     * 下单
     * @param request
     * @return
     */
    @PostMapping(value = "order/confirm")
    public ResResult confirm(@RequestBody OrderConfirmRequest request){
        //调用订单下单相关逻辑
        if(StringUtils.isEmpty(request.getRequestId())){
            return ResResult.getSysError("请求ID不能为空!");
        }
        if(request.getRequestId().length() != 36){
            return ResResult.getSysError("请求ID格式错误!");
        }
        //检查当前请求唯一ID,是否已经存在,如果存在,再提交就是重复下单
        Order source = orderService.queryByRequestId(request.getRequestId());
        if(Objects.nonNull(source)){
            return ResResult.getSysError("当前订单已经提交成功,请勿重复提交");
        }
        orderService.confirm(request);
        return ResResult.getSuccess();
    }
}

如果是并发请求也不用担心,因为数据库表已经设置了唯一索引,尤其只有一条有效数据会插入成功,可以防止重复的数据产生。

对于下单流量不算高的系统,可以采用这种请求唯一ID+数据表增加唯一索引约束的方式,来防止接口重复提交!

虽然简单粗暴,但是十分有效!

可能有的人会问,看上面的代码生成请求唯一 ID 很简单,为啥不直接前端生成一个请求唯一ID,然后提交呢?

之所以把获取请求唯一ID的生成规则放在后端,好处就是生成规则可以自己定义,也并不一定要用uuid来生成,也可以用雪花算法,或者自己设计一套计算规则,保证当前业务提交时请求ID是唯一的,比如事先生成唯一的订单号,作为请求唯一ID,然后再提交,规则放在后端来生成,会更加灵活!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK