一.前言
繼前面四篇ASP.NET MVC的隨筆,我們繼續向下學習。上一節我們學習了關於控制器的使用,本節我們將要學習如何使用過濾器控制用戶訪問頁面。
二.正文
以下的示例建立在ASP.NET MVC 4之上(VS2012)
1.授權過濾器
只要涉及用戶的網站,都一定會涉及到什么權限的用戶可以訪問哪個頁面。對於新手而言可能都在每個頁面中單獨寫這個功能方法,導致的后果就是大量重復的代碼,並且不便於以后的變動。有用一定經驗之后,就會采用集中控制的方式,讓所有的頁面先執行特定的方法去判斷,這樣的優點就是減少了代碼的重復,同時也能夠靈活的配置。但是對於某些擁有特殊需求的頁面就有點力所不能及,而且這種權限判斷的代碼會和頁面的邏輯代碼混為一體,難以區分。而今天介紹的授權過濾器是采用注解屬性的方式去控制每個動作,並且可以靈活的配置。
首先我們在網站根目錄新建一個Filter文件夾,在其中新建一個CustAuthorizeAttribute類,類中的具體代碼如下所示:
1 namespace MvcStudy.Filter 2 { 3 public class CustAuthorizeAttribute : AuthorizeAttribute 4 { 5 private string[] roles; 6 7 public CustAuthorizeAttribute(params String[] role) 8 { 9 roles = role; 10 } 11 12 protected override bool AuthorizeCore(HttpContextBase httpContext) 13 { 14 String role = httpContext.Request.QueryString["role"]; 15 if (role != null) 16 { 17 return roles.Contains(role); 18 } 19 return base.AuthorizeCore(httpContext); 20 } 21 } 22 }
通過上面的代碼我們知道這個過濾器將會在使用時要求使用者傳入可以訪問的角色有哪些,其中最終的是AuthorizeCore方法,該方法是負責判斷當前的請求是有具有權限,這里我們為了演示,所以直接根據查詢字符串的role的值來判斷。
既然我們已經有了過濾器,下面我們新建一個Home控制器,並在Views下新建一個Home文件夾,同時在Home文件夾下新建名為Index的視圖以及List的視圖。接着我們打開Home控制器。
在其中寫入如下代碼:
1 namespace MvcStudy.Controllers 2 { 3 public class HomeController : Controller 4 { 5 [CustAuthorize("vip")] 6 public ActionResult Index() 7 { 8 return View(); 9 } 10 11 [CustAuthorize("admin")] 12 public ActionResult List() 13 { 14 return View(); 15 } 16 } 17 }
我們可以看到筆者將Index動作規定為只有role為vip才能訪問,而List則是admin。接着我們運行項目,瀏覽器會默認打開http://localhost:7575/(端口請根據實際情況而定),但是你會發現你將會被轉移到http://localhost:7575/Account/Login?ReturnUrl=%2f這個頁面,因為項目中我們並沒有新建這個視圖,所以會報404錯誤。而這個是被通過授權后ASP.NET MVC默認的行為,當然我們可以改變這個行為,下面我們來改變這個行為,讓其直接跳轉到Login視圖,修改CustAuthorizeAttribute類。
代碼如下所示:
1 namespace MvcStudy.Filter 2 { 3 public class CustAuthorizeAttribute : AuthorizeAttribute 4 { 5 private string[] roles; 6 7 public CustAuthorizeAttribute(params String[] role) 8 { 9 roles = role; 10 } 11 12 protected override bool AuthorizeCore(HttpContextBase httpContext) 13 { 14 String role = httpContext.Request.QueryString["role"]; 15 if (role != null) 16 { 17 return roles.Contains(role); 18 } 19 return base.AuthorizeCore(httpContext); 20 } 21 22 protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) 23 { 24 UrlHelper url = new UrlHelper(filterContext.RequestContext); 25 filterContext.Result = new RedirectResult("/Login"); 26 } 27 } 28 }
其中關鍵的是我們重寫了HandleUnauthorizedRequest方法,這個方法只有在AuthorizeCore返回false情況才會調用,同時該方法的參數中有一個Result屬性,該屬性是將最終的處理結果賦給它。
重新編譯,我們繼續訪問默認的路徑,這個時候我們可以發現轉移的路徑已經變成了http://localhost:7575/Login,到這里我們已經可以處理授權失敗的情況了,現在我們就要開始測試幾個正確的結果首先是Index,我們只需要訪問該路徑即可http://localhost:7575/Home/Index?role=vip(同http://localhost:7575/?role=vip),如果要訪問List,我們就需要訪問這個路徑http://localhost:7575/Home/List?role=admin,通過這個簡單的例子,我們就可以知道在ASP.NET MVC中是通過何種方式去控制用戶訪問頁面的權限。
2.異常過濾器
在我們的開發過程我們很多的時間都在修改異常,防止異常的發生。但是在實際的運行過程中總會發生很多我們未曾發現的問題,以及其他惡意的攻擊。這個時候我們就需要一個統一的機制可以控制並處理這些異常,或許我一說到異常很多人都會聯想到try{}catch{},的確沒錯,異常的捕獲是用他們,但是你有沒有想過,如果一個網站導出充斥着try{}catch{},不僅僅是不美觀,同時也能以控制。而ASP.NET MVC為我們提供了異常過濾器,可以為每個動作添加這個注解屬性,這樣我們就可以靈活的控制並處理這些異常。
首先我們在Filter文件中新建一個CustExceptionAttribute類,並在其中寫入如下的代碼:
1 namespace MvcStudy.Filter 2 { 3 public class CustExceptionAttribute : FilterAttribute , IExceptionFilter 4 { 5 public void OnException(ExceptionContext filterContext) 6 { 7 if (!filterContext.ExceptionHandled) 8 { 9 filterContext.Result = new RedirectResult("error.html"); 10 filterContext.ExceptionHandled = true; 11 } 12 } 13 } 14 }
我們可以看到這里我們依然還是用Result來將我們處理后的結果賦值給它,但是我們這里還判斷了ExceptionHandled屬性,這個屬性的含義是如果其他的異常過濾器已經處理了該異常,則該屬性的值為true,為了能夠適應大的范圍,所以筆者這里建議讀者也要先判斷是否已經有其他的異常過濾器已經處理過這個異常了。
下面我們在Home控制器中的Index動作中使用:
1 namespace MvcStudy.Controllers 2 { 3 public class HomeController : Controller 4 { 5 [CustException()] 6 public ActionResult Index() 7 { 8 throw new NullReferenceException(); 9 return View(); 10 } 11 } 12 }
這個時候我們訪問http://localhost:7575/將會跳轉至http://localhost:7575/error.html頁面(因為筆者沒有建這個頁面所以會顯示404),但是讀者可以看到這里我僅僅只是根據異常跳轉到一個特定的頁面,其實ASP.NET MVC中已經默認為我們實現了這個注解屬性,這個注解屬性就是HandleError類,我們只需要傳入需要捕獲的異常類型,以及對應的頁面即可。
下面我們修改Home控制器的Index動作:
1 namespace MvcStudy.Controllers 2 { 3 public class HomeController : Controller 4 { 5 [HandleError(ExceptionType=typeof(NullReferenceException),View="error")] 6 public ActionResult Index() 7 { 8 throw new NullReferenceException(); 9 return View(); 10 } 11 } 12 }
然后我們重新編譯,並訪問頁面,會發現頁面沒有按照我們的預想跳轉到特定的頁面而是直接顯示了錯誤的詳情。這是因為ASP.NET MVC默認情況下沒有開啟自定義異常處理,所以就會出現這個默認的頁面,下面我們修改Web.Config,在其中添加如下的配置:

這時我們重新編譯刷新頁面,但是我們會發現頁面會有內容,因為這個頁面調用的是Views/Shared/Error.cshtml頁面。
未完待續…
