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


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

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

    自定义注解+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之后再点击接口,发现又响应了正常内容。

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

  • 相关阅读:
    比赛:小奔的方案 solution
    比赛:小奔的矩形solution
    比赛:小奔与不等四边形solution
    NOIP2018普及T2暨洛谷P5016 龙虎斗
    Java-GUI基础(三)java.swing
    Java-GUI基础(二)java.awt
    Java-GUI基础(一)
    Java集合(类)框架(三)
    Java集合(类)框架(二)
    Java集合(类)框架(一)
  • 原文地址:https://www.cnblogs.com/zhixie/p/13185695.html
Copyright © 2020-2023  润新知