返回

前后端合作,防接口幂等性重复提交,从入门到实战

后端

使用SpringBoot自定义注解+AOP+Redis实现防接口幂等性

在构建健壮和用户友好的应用程序时,防接口幂等性至关重要。它可以防止由于用户重复提交相同的请求而导致意外后果,从而确保应用程序的数据完整性和用户体验。

什么是接口幂等性?

接口幂等性是一种特性,即使接口被多次调用,它也只会执行一次,从而产生相同的结果。换句话说,无论用户有意无意地重复提交请求,接口都只会响应第一次请求。

为什么需要防接口幂等性?

在实际场景中,接口幂等性可以防止多种问题:

  • 意外数据重复: 用户在网络不稳定或系统延迟的情况下可能无意中重复提交请求,导致数据库中出现重复数据。
  • 恶意攻击: 恶意用户可能利用接口的幂等性缺陷,通过反复提交请求来耗尽系统资源或操纵数据。
  • 用户体验不佳: 重复提交请求可能会导致用户界面出现错误消息或意外行为,从而影响用户体验。

如何使用SpringBoot自定义注解+AOP+Redis实现防接口幂等性?

我们采用一种综合的方法,结合SpringBoot自定义注解、AOP(面向方面编程)和Redis,来实现防接口幂等性。

1. SpringBoot自定义注解

首先,我们定义一个SpringBoot自定义注解,即@ApiIdempotent,用于标记需要进行幂等性控制的接口方法。这个注解包含一个可选的参数value,可以指定一个唯一的键来标识幂等性检查。

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiIdempotent {
    String value() default "";
}

2. AOP

接下来,我们使用AOP来拦截所有被@ApiIdempotent注解标记的接口方法。通过在切面类中实现@Around方法,我们可以环绕目标方法执行自定义逻辑。

@Aspect
@Order(1)
public class ApiIdempotentAspect {

    @Pointcut("@annotation(com.example.demo.annotation.ApiIdempotent)")
    public void pointcut() {
    }

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        String key = "api_idempotent:" + joinPoint.getSignature().toString();
        boolean flag = redisTemplate.opsForValue().setIfAbsent(key, "1", 10, TimeUnit.SECONDS);
        if (flag) {
            try {
                return joinPoint.proceed();
            } finally {
                redisTemplate.delete(key);
            }
        } else {
            throw new ApiException("接口幂等性校验失败");
        }
    }
}

在切面方法中,我们生成了一个唯一的键来标识幂等性检查,并使用Redis的setIfAbsent方法将其存入Redis。如果键不存在,则表示接口请求是第一次提交,我们执行目标方法并最终删除Redis中的键。否则,如果键已存在,则抛出异常,防止接口被重复执行。

3. Redis

最后,我们使用Redis作为分布式缓存,存储幂等性检查的键。Redis的键值对特性和快速的读取/写入操作使其成为实现幂等性控制的理想选择。

实战演示

为了演示如何使用这种方法实现防接口幂等性,让我们创建一个接口:

@RestController
@RequestMapping("/api")
public class ApiController {

    @PostMapping("/test")
    @ApiIdempotent
    public String test() {
        return "success";
    }
}

启动应用程序后,我们可以使用HTTP请求工具(如Postman)发送多次请求到/api/test端点。无论我们发送多少次请求,我们都只会收到一次响应,即"success"

结论

通过结合SpringBoot自定义注解、AOP和Redis,我们可以有效地实现防接口幂等性。这种方法可扩展、健壮且易于实现,有助于防止意外数据重复、恶意攻击和用户体验不佳。

常见问题解答

  1. 为什么我们需要自定义注解?
    自定义注解允许我们标记需要进行幂等性控制的接口方法,从而提供一种声明式的方式来实现幂等性。

  2. AOP如何帮助我们拦截方法?
    AOP是一种面向方面编程的技术,它允许我们环绕目标方法执行自定义逻辑。通过使用切点,我们可以拦截被@ApiIdempotent注解标记的所有方法。

  3. 为什么使用Redis来存储键?
    Redis是一种分布式缓存,具有快速的读取/写入操作。它可以可靠地存储幂等性检查的键,并确保在多个服务器实例之间保持一致性。

  4. 键值是如何生成的?
    键值由接口方法的签名和可选的自定义值(通过@ApiIdempotent注解的value参数)组合生成。这样可以确保每个接口方法都有一个唯一的键。

  5. 如果Redis不可用会发生什么?
    如果Redis不可用,我们仍然可以执行目标方法,但幂等性控制将无法正常工作。在这种情况下,我们应该考虑使用替代机制,如本地缓存或数据库锁。