MVC3這個開發框架還是很受大家歡迎的,我個人也非常喜歡,逢人總要誇獎一番,雖然還有很多地方需要完善,但總體上來說很棒!
不過,最近碰到一個比較糾結的問題,在點擊注冊時,為了使用一些友好的前端交互效果,所以使用 Ajax.BeginForm的方式,代碼如下:
{...
}
分別通過 RegOngoing和 RegDone 執行注冊過程中和完成后的前端提示效果。
但這種方式經常會在數據庫中出現重復的數據,分析了一下原因,初步判斷應該是注冊事件被重復提交了,雖然在后端邏輯層會檢測數據是否存在,但其執行過程中本質上是會順序執行各種邏輯,所以在前一條還沒有插入數據庫,后一天已經被提交過來被檢測的概率是存在的,尤其是如果在數據訪問層端有較長的事務性邏輯存在的話,那這種重復提交的概率會更高!
采取的第一套方法:就是在用戶點擊后將注冊按鈕disable掉,並給與友好提示,避免用戶去重復點擊注冊...
$( " #Register ").attr( " disabled ", true);
$( " #Register ").val( " 信息提交中... ");}
這在很長一段時間內我們都認為解決了這個問題,可是在月黑分高的一個晚上,重復數據又出現了,這下糾結了,難道是被黑了?
利用一切可利用的資源,google、baidu、博客園、qq群等等到處找問題根源,都沒有很好的結果。最近團隊開會,仔細的分析了一下,發覺問題可能還是出在上面分析過的可能性,比如:很多用戶有雙擊的習慣,很多用戶有連續按回答的習慣...,只要RegOngoing反應遲鈍一點,就還是會存在這種被重復提交注冊的概率,雖然概率很小,但問題不能放過去,針對此種可能性,我們對這個注冊場景進行了無思考時間的壓力測試,發覺果真還是會被重復提交。
這樣,第二套方案應運而生: 采用token預防機制,在Register的action中增加一個基於IP的token策略,並在注冊邏輯執行完成后刪除token(這里不管是否成功,都必須刪除哦,不然你再想注冊就有可能不行)。
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我或者留言給我吧