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我或者留言给我吧 |