注册登录过程点滴(三):解决MVC3中使用Ajax.BeginForm会重复提交数据的问题

MVC3这个开发框架还是很受大家欢迎的,我个人也非常喜欢,逢人总要夸奖一番,虽然还有很多地方需要完善,但总体上来说很棒!

不过,最近碰到一个比较纠结的问题,在点击注册时,为了使用一些友好的前端交互效果,所以使用 Ajax.BeginForm的方式,代码如下:

@using (Ajax.BeginForm("Register""Account"new AjaxOptions { UpdateTargetId = "ResultPannel", OnBegin = "RegOngoing", OnSuccess = "RegDone" }))
{...

分别通过 RegOngoing和 RegDone 执行注册过程中和完成后的前端提示效果。

但这种方式经常会在数据库中出现重复的数据,分析了一下原因,初步判断应该是注册事件被重复提交了,虽然在后端逻辑层会检测数据是否存在,但其执行过程中本质上是会顺序执行各种逻辑,所以在前一条还没有插入数据库,后一天已经被提交过来被检测的概率是存在的,尤其是如果在数据访问层端有较长的事务性逻辑存在的话,那这种重复提交的概率会更高!

采取的第一套方法:就是在用户点击后将注册按钮disable掉,并给与友好提示,避免用户去重复点击注册...

function RegOngoing() {
   $("#Register").attr("disabled"true);
   $("#Register").val("信息提交中...");}

 这在很长一段时间内我们都认为解决了这个问题,可是在月黑分高的一个晚上,重复数据又出现了,这下纠结了,难道是被黑了?

利用一切可利用的资源,google、baidu、博客园、qq群等等到处找问题根源,都没有很好的结果。最近团队开会,仔细的分析了一下,发觉问题可能还是出在上面分析过的可能性,比如:很多用户有双击的习惯,很多用户有连续按回答的习惯...,只要RegOngoing反应迟钝一点,就还是会存在这种被重复提交注册的概率,虽然概率很小,但问题不能放过去,针对此种可能性,我们对这个注册场景进行了无思考时间的压力测试,发觉果真还是会被重复提交。

这样,第二套方案应运而生: 采用token预防机制,在Register的action中增加一个基于IP的token策略,并在注册逻辑执行完成后删除token(这里不管是否成功,都必须删除哦,不然你再想注册就有可能不行)。


[HttpPost]

public ActionResult Register(RegisterModel reg)
        {
            //防止用户并发提交数据
            string key = StringHelper.GetClientIP();
            if (!string.IsNullOrEmpty(RegTokenHelper.findTokenByKey(key)))
            {
                return Content("errorSubmit");
            }
            //生成用户Token
            RegTokenHelper.generateToken(key);

            if (IsValidateCode(reg.sCode) == false)
            {
                //删除Token
                RegTokenHelper.deleteToken(key);
                return Content("errorCode");
            }

            ...
                        
            //删除Token
            RegTokenHelper.deleteToken(key);
            return Content(url);
        }

 这里主要是用本机的IP作为本机唯一访问的标识,至于token值可以自己生成,也可以用系统的,我们采用的是guid值。

至此,这个问题基本上可以告一段落了!但是其实不知道大家发现没有,上述方法中还存在一定的问题,或者注册失败的风险,就是当用户在同一个局域网内并发的进行注册,那是有可能失败的,因为其token是相同的,后者会被认为是已经正在注册!

这种场景其实也是有解决方法,就是不要使用IP作为key,而采用session。根据我们网站的用户特点,前者发生的概率实在是微乎其微,而且本质上没有什么危害性,所以我们没有采用session,而默认接受这种风险了^_^

 至于大家要追求完美,那就无可非议了! 同时希望MVC的团队能从框架性上去解决这个问题,因为这种场景是在是太多啦!!!

如果大牛们还有更好的办法,Q我或者留言给我吧 

原文地址:https://www.cnblogs.com/jivenbest/p/2609929.html