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

栏目: IT技术 · 发布时间: 5年前

内容简介:今天早上,新来的同事小王突然问我:“周哥,什么是幂等性啊?”。然后我就跟他解释了一番,幂等性就是说无论你执行几次请求,其结果是一样的。说到了幂等就不得不说重复提交了,你连续点击提交按钮,理论上来说这是同一条数据,数据库应该只能存入一条,而实际上存放了多条,这就违反了幂等性。因此我们就需要做一些处理,来保证连续点击提交按钮后,数据库只能存入一条数据。防止重复提交的方式很多,这里我就说一下我认为比较好用的一种。我们通过获取用户ip及访问的接口来判断他是否重复提交,假如这个ip在一段时间内容多次访问这个接口,我们

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

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

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

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

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


以上所述就是小编给大家介绍的《重复提交,你是如何处理的?》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Don't Make Me Think

Don't Make Me Think

Steve Krug / New Riders Press / 18 August, 2005 / $35.00

Five years and more than 100,000 copies after it was first published, it's hard to imagine anyone working in Web design who hasn't read Steve Krug's "instant classic" on Web usability, but people are ......一起来看看 《Don't Make Me Think》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具