前兩天園友JustRun分享了一篇 《菜鳥程序員之Asp.net MVC Session過期異常的處理》博文,正好自己前段時間被安排處理過這個問題,發現JustRun的方法有一點點可優化的地方,就評論里提了一下思路。今天看到有園友好像沒明白我說的意思,所以就決定寫此博文簡單介紹一下,不太適合老鳥們觀看,權當是給新人寫的一點經驗分享。
什么問題
通常在對一些敏感數據進行操作前,都要進行身份認證。如果沒有通過認證,則重定向(跳轉)到登錄頁面。但這種服務端重定義的方式,對ajax請求卻沒有任何作用。本文就是給出一種全局處理方式,來解決這類問題。
如何解決
思路很簡單,就是當授權過期后,針對ajax請求返回一個特定的標識,然后前端通過識別該標識,來跳轉到登錄頁面。而且最好不用在每處ajax請求的代碼中都加上對這種標識判斷,即要能全局處理。所以目標就落在了jQuery.ajax的全局配置($.ajaxSetup)上了,通過查看API,發現statusCode參數用來做這件事再好不過了,而且重要的是,即使ajax代碼中禁用了全局配置(global:false),關於statusCode的配置都仍然有效(這點對我們之前項目中來說很重要,因為有很多的ajax都禁用了全局的遮罩效果)。
代碼演示
原理清楚了,代碼實現起來也就並不復雜了,首先在服務端需要針對ajax請求,返回特殊的標識。這里重寫了MVC自帶的授權模塊的HandleUnauthorizedRequest邏輯,未通過授權時,如果是ajax請求,則返回一個HttpStatusCode,這個值盡量不要與現有的HTTP狀態碼重復,我這里用的是499,然后將登錄頁的地址寫到響應流中(這里可以根據需要返回其他信息)。自定義的授權模塊代碼如下:
public class WebAuthorizeAttribute : AuthorizeAttribute { protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) { if (filterContext.HttpContext.Request.IsAjaxRequest()) { filterContext.Result = new HttpStatusCodeResult(499); //盡量不要與現有的Http狀態碼沖突 filterContext.HttpContext.Response.Write(FormsAuthentication.LoginUrl); //可以根據需要返回其他信息 } else { base.HandleUnauthorizedRequest(filterContext); } } }
然后就是前端部分的處理了,前端使用$.ajaxSetup的statusCode參數來統一處理,在模板頁面中,寫入以下代碼:
<script type="text/javascript"> $.ajaxSetup({ statusCode: { 499: function (data) { alert(data.responseText); //window.location.href = data.responseText; } } }); </script>
好了,所有的代碼都完成了,下面進行簡單的測試一下,服務端寫了兩個Action(這兩個Action需要用上面寫的授權模塊),一個返回Time,一個返回Guid:
[WebAuthorize] public ActionResult GetTime() { return Content(DateTime.Now.ToString()); } [WebAuthorize] public ActionResult GetGuid() { return Content(Guid.NewGuid().ToString()); }
頁面上寫了兩個ajax請求,分別請求以上兩個Action:
<input id="Button1" type="button" value="GetTime" /> <input id="Button2" type="button" value="GetGuid" /> <script type="text/javascript"> $("#Button1").click(function () { $.get("/Home/GetTime", function (data) { $("body").append(data + '<br/>'); }); }); $("#Button2").click(function () { $.ajax({ url: '/Home/GetGuid', global: false, //禁用全局,不影響$.ajaxSetup中的statusCode success: function (data) { $("body").append(data + '<br/>'); } }); }); </script>
當然還要添加登錄退出功能(這塊代碼就不貼了,見源碼吧),在授權有效期內,運行效果如下:
這時,如果我們再打開一個新頁面點退出(或直接把cookie刪掉),再點擊上面的GetTime或GetGuid按鈕發送ajax請求,結果如下:
OK,達到預期效果。如有更優雅的處理方式,也請大家分享。DEMO使用VS2010+MVC3開發,源碼請點此下載。