標籤:bar auth 記錄 format 對象 技術 copy 結果 優先順序
APS.NET MVC中(以下簡稱“MVC”)的每一個請求,都會分配給相應的控制器和對應的行為方法去處理,而在這些處理的前前後後如果想再加一些額外的邏輯處理。這時候就用到了過濾器。
MVC支援的過濾器類型有四種,分別是:Authorization(授權),Action(行為),Result(結果)和Exception(異常)。如下表,
過濾器類型 |
介面 |
描述 |
Authorization |
IAuthorizationFilter |
此類型(或過濾器)用於限制進入控制器或控制器的某個行為方法 |
Exception |
IExceptionFilter |
用於指定一個行為,這個被指定的行為處理某個行為方法或某個控制器裡面拋出的異常 |
Action |
IActionFilter |
用於進入行為之前或之後的處理 |
Result |
IResultFilter |
用於返回結果的之前或之後的處理 |
但是預設實現它們的過濾器只有三種,分別是Authorize(授權),ActionFilter,HandleError(錯誤處理);各種資訊如下表所示
過濾器 |
類名 |
實現介面 |
描述 |
ActionFilter |
AuthorizeAttribute |
IAuthorizationFilter |
此類型(或過濾器)用於限制進入控制器或控制器的某個行為方法 |
HandleError |
HandleErrorAttribute |
IExceptionFilter |
用於指定一個行為,這個被指定的行為處理某個行為方法或某個控制器裡面拋出的異常 |
自訂 |
ActionFilterAttribute |
IActionFilter和IResultFilter |
用於進入行為之前或之後的處理或返回結果的之前或之後的處理 |
下面介紹的過濾器中,除了上面這幾種外,還多加一種過濾器OutputCache
1 授權過濾器Authorize
1.1 預設Authorize使用
現在在網上無論是要求身分識別驗證的地方多得是,發郵件,買東西,有時候就算吐個槽都要提示登入的。這裡的某些操作,就是要經過驗證授權才被允許。在MVC中可以利用Authorize來實現。例如一個簡單的修改密碼操作
[Authorize] public ActionResult ChangePassword() { return View(); }
它需要使用者通過了授權才能進入到這個行為方法裡面,否則硬去請求那個頁面的話,只會得到這個結果
如果要通過驗證,通過調用FormsAuthentication.SetAuthCookie方法來獲得授權,登陸的頁面如下
@model FilterTest.Models.LogInModel@{ Layout = null;}<!DOCTYPE html><html><head> <title>Login</title></head><body> <div> @using( Html.BeginForm()){ <div> ID:@Html.TextBoxFor(m=>m.UserName) <br /> Password:@Html.PasswordFor(m => m.Password) <br /> <input type="submit" value="login" /> </div> } </div></body></html>
行為方法如下
[HttpPost]//這裡用了謂詞過濾器,只處理POST的請求 public ActionResult Login(LogInModel login) { if (login.UserName == "admin" && login.Password == "123456") { FormsAuthentication.SetAuthCookie(login.UserName, false); return Redirect("/Customer/ChangePassword"); } return View(); }
當然有登入也要有登出,因為登出是在登陸之後發生的,沒登陸成功也就沒有登出,所以登出的行為方法也要加上Authorize過濾器,登出調用的是FormsAuthentication.SignOut方法,代碼如下
[Authorize] public ActionResult LogOut() { FormsAuthentication.SignOut(); return Redirect("/Customer/Login"); }
1.2 自訂授權
我們不一定要用MVC預設的Authorize授權驗證規則,規則可以自己來定,自訂授權過濾器可以繼承AuthorizeAttribute這個類,這個類裡面有兩個方法是要重寫的
- bool AuthorizeCore(HttpContextBase httpContext):這裡主要是授權驗證的邏輯處理,返回true的則是通過授權,返回了false則不是。
- void HandleUnauthorizedRequest(AuthorizationContext filterContext):這個方法是處理授權失敗的事情。
這裡就定義了一個比較騎呢的授權處理器,當請求的時候剛好是偶數分鐘的,就通過可以獲得授權,反之則不通過。當授權失敗的時候,就會跳轉到登陸頁面了。
public class MyAuthorizeAttribute:AuthorizeAttribute { protected override bool AuthorizeCore(HttpContextBase httpContext) { //return base.AuthorizeCore(httpContext); return DateTime.Now.Minute % 2 == 0 } protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) { filterContext.HttpContext.Response.Redirect("/Customer/Login"); //base.HandleUnauthorizedRequest(filterContext); } }
然後用到一個行為方法上,
[MyAuthorize] public ActionResult ShowDetail() { return View(); }
每當偶數分鐘的時候就可以訪問得到這個ShowDetail的視圖,否則就會跳到了登陸頁面了。
2 處理錯誤過濾器HandleError
2.1 預設HandleError使用
在往常的開發中,想到異常處理的馬上就會想到try/catch/finally語句塊。在MVC裡面,萬一在行為方法裡面拋出了什麼異常的,而那個行為方法或者控制器有用上HandleError過濾器的,異常的資訊都會在某一個視圖顯示出來,這個顯示異常資訊的視圖預設是在Views/Shared/Error
這個HandleError的屬性如下
屬性名稱 |
類型 |
描述 |
ExceptionType |
Type |
要處理的異常的類型,相當於Try/Catch語句塊裡Catch捕捉的類型,如果這裡不填的話則表明處理所有異常 |
View |
String |
指定需要展示異常資訊的視圖,只需要視圖名稱就可以了,這個視圖檔案要放在Views/Shared檔案夾裡面 |
Master |
String |
指定要使用的母片視圖的名稱 |
Order |
Int |
指定過濾器被應用的順序,預設是-1,而且優先順序最高的是-1 |
這個Order屬性其實不只這個HandleError過濾器有,其優先順序規則跟其他過濾器的都是一樣。
下面則故意弄一個會拋異常的行為方法
[HandleError(ExceptionType = typeof(Exception))] public ActionResult ThrowErrorLogin() { throw new Exception("this is ThrowErrorLogin Action Throw"); }
光是這樣還不夠,還要到web.config檔案中的<system.web>一節中添加以下代碼
<customErrors mode="On" />
因為預設的開發模式中它是關閉的,要等到部署到伺服器上面才會開啟,讓異常資訊比較友好的用一個視圖展現。
像這裡訪問ThrowErrorLogin視圖時,由於拋出了一次,就轉到了一個特定的視圖
在這裡看到的異常視圖是這樣的,除了用這個建項目時預設產生的異常視圖之外,我們還可以自己定義異常視圖,視圖裡面要用到的異常資訊,可以通過@Model擷取,它是一個ExceptionInfo類型的執行個體,例如這裡建了一個異常視圖如下
@{ Layout = null;}<!DOCTYPE html><html><head> <title>MyErrorPage</title></head><body> <div> <p> There was a <b>@Model.Exception.GetType().Name</b> while rendering <b>@Model.ControllerName</b>‘s <b>@Model.ActionName</b> action. </p> <p style="color:Red"> <b>@Model.Exception.Message</b> </p> <p>Stack trace:</p> <pre style=" padding: 0px; color: rgb(0, 0, 255); line-height: 1.5 !important;">>@Model.Exception.StackTrace</pre> </div></body></html>
它存放的路徑是~/Views/Shared裡面,像上面的行為方法如果要用異常資訊渲染到這個視圖上面,在控制器的處改成這樣就可以了
[HandleError(ExceptionType = typeof(Exception), View = "MyErrorPage")]
2.2 自訂錯誤異常處理
這裡的錯誤處理過濾器也可以自己來定義,做法是繼承HandleErrorAttribute類,重寫void OnException(ExceptionContext filterContext)方法,這個方法調用是為了處理未處理的異常,例如
public override void OnException(ExceptionContext filterContext) { //base.OnException(filterContext); if (!filterContext.ExceptionHandled && filterContext.Exception.Message == "this is ThrowErrorLogin Action Throw") { filterContext.ExceptionHandled=true; filterContext.HttpContext.Response.Write("5洗ten No Problem<br/>" + filterContext.Exception.ToString()); } }
這裡用到的傳入了一個ExceptionContext的對象,既可以從它那裡獲得請求的資訊,又可以擷取異常的資訊,它部分屬性如下
屬性名稱 |
類型 |
描述 |
ActionDescriptor |
ActionDescriptor |
提供詳細的操作方法 |
Result |
ActionResult |
結果的操作方法,過濾器可以取消,要求將此屬性設定為一個非空值 |
Exception |
Exception |
未處理的異常 |
ExceptionHandled |
bool |
另一個過濾器,則返回true,如果有明顯的異常處理 |
這裡的ExceptionHandler屬性要提一下的是,如果這個異常處理完的話,就把它設為true,那麼即使有其他的錯誤處理器捕獲到這個異常,也可以通過ExceptionHandler屬性判斷這個異常是否經過了處理,以免重複處理一個異常錯誤而引發新的問題。
3 OutputCache過濾器
OutputCache過濾器用作緩衝,節省使用者訪問應用程式的時間和資源,以提高使用者體驗,可這個我實驗試不出它的效果。留作筆記記錄一下。OutputCacheAttribute這個類有以下屬性
屬性名稱 |
類型 |
描述 |
Duration |
int |
緩衝的時間,以秒為單位,理論上緩衝時間可以很長,但實際上當系統資源緊張時,緩衝空間還是會被系統收回。 |
VaryByParam |
string |
以哪個欄位為標識來快取資料,比如當“ID”欄位變化時,需要改變緩衝(仍可保留原來的緩衝),那麼應該設VaryByParam為"ID"。這裡你可以設定以下幾個值: * = 任何參數變化時,都改變緩衝。 none = 不改變緩衝。 以分號“;”為間隔的欄位名列表 = 列表中的欄位發生變化,則改變緩衝。 |
Location |
OutputCacheLocation |
快取資料放在何處。預設是Any,其他值分別是Client,Downstream,Server,None,ServerAndClient |
NoStore |
bool |
用於決定是否阻止敏感資訊的二級儲存。 |
例如一個OutputCache過濾器可以這樣使用
[OutputCache(Location= System.Web.UI.OutputCacheLocation.Client,Duration=60)] public ActionResult Login() { return View(); }
或者有另外一種使用方式——使用設定檔,在<system.web>節點下添加以下設定
<caching> <outputCacheSettings> <outputCacheProfiles> <add name="testCache" location="Client" duration="60"/> </outputCacheProfiles> </outputCacheSettings> </caching>
使用控制的時候就這樣
[OutputCache(CacheProfile="testCache")] public ActionResult Login() { return View(); }
4 自訂過濾器
萬一前面介紹的過濾器也滿足不了需求,要在行為方法執行返回的前前後後定義自己的處理邏輯的話,這個自訂過濾器就應該能派上用場了。若要自訂一個過濾器,則要繼承ActionFilterAttribute類,這個類是一個抽象類別,實現了IActionFilter和IResultFilter介面,主要通過重寫四個虛方法來達到在行為方法執行和返回的前後注入邏輯
方法 |
參數 |
描述 |
OnActionExecuting |
ActionExecutingContext |
在行為方法執行前執行 |
OnActionExecuted |
ActionExecutedContext |
在行為方法執行後執行 |
OnResultExecuting |
ResultExecutingContext |
在行為方法返回前執行 |
OnResultExecuted |
ResultExecutedContext |
在行為方法返回後執行 |
四個方法執行順序是OnActionExecuting——>OnActionExecuted——>OnResultExecuting——>OnResultExecuted。上面四個方法的參數都是繼承基ContollorContext類。例如下面定義了一個自訂的過濾器
public class MyCustomerFilterAttribute : ActionFilterAttribute { public string Message { get; set; } public override void OnActionExecuted(ActionExecutedContext filterContext) { base.OnActionExecuted(filterContext); filterContext.HttpContext.Response.Write(string.Format( "<br/> {0} Action finish Execute.....",Message)); } public override void OnActionExecuting(ActionExecutingContext filterContext) { CheckMessage(filterContext); filterContext.HttpContext.Response.Write(string.Format("<br/> {0} Action start Execute.....", Message)); base.OnActionExecuting(filterContext); } public override void OnResultExecuted(ResultExecutedContext filterContext) { filterContext.HttpContext.Response.Write(string.Format("<br/> {0} Action finish Result.....", Message)); base.OnResultExecuted(filterContext); } public override void OnResultExecuting(ResultExecutingContext filterContext) { filterContext.HttpContext.Response.Write(string.Format("<br/> {0} Action start Execute.....", Message)); base.OnResultExecuting(filterContext); } private void CheckMessage(ActionExecutingContext filterContext) { if(string.IsNullOrEmpty( Message)||string.IsNullOrWhiteSpace(Message)) Message = filterContext.Controller.GetType().Name + "‘s " + filterContext.ActionDescriptor.ActionName; } }
使用它的行為方法定義如下
[MyCustomerFilter] public ActionResult CustomerFilterTest() { Response.Write("<br/>Invking CustomerFilterTest Action"); return View(); }
執行結果如下
這個就證明了上面說的順序。
當控制器也使用上這過濾器時,而行為方法不使用時,結果如下
如果控制器和行為方法都使用了過濾器,理論上是顯示上面兩個結果的有機結合。但實際不然,因為在定義過濾器的時候還少了一個特性:[AttributeUsage(AttributeTargets.All, AllowMultiple = true)],把這個加在MyCustomerFilterAttribute就行了。
[AttributeUsage(AttributeTargets.All, AllowMultiple = true)]//多次調用 public class MyCustomerFilterAttribute : ActionFilterAttribute { …… }
由這幅圖可以看出,同一個過濾器分別用在了控制器和行為方法中,執行同一個方法時都會有先後順序,如果按預設值(不設Order的情況下),一般的順序是由最外層到最裡層,就是“全域”——>“控制器”——>“行為方法”;而特別的就是錯誤處理的過濾器,由於異常是由裡往外拋的,所以它的順序剛好也反過來:“行為方法”——>“控制器”——>“全域”。
既然這裡有提到全域的過濾器,那麼全域的過濾器是在Global.asax檔案裡面的RegisterGlobalFilters(GlobalFilterCollection filters)中設定的
public static void RegisterGlobalFilters(GlobalFilterCollection filters) { filters.Add(new HandleErrorAttribute()); filters.Add(new MyFilters.MyCustomerFilterAttribute() { Message="Global"});//全域過濾器 }
C# MVC 過濾器