深入理解ASP.NET MVC(8)

來源:互聯網
上載者:User

系列目錄

過濾器上下文參數

前一節提到了四種MVC內建過濾器,它們無一例外都在關鍵的方法中提供了叫filterContext的參數,儘管它們各自類型不同,但是都繼承自ControllerContext。

其中一個共同的重要屬性是:

public ActionResult Result {    get;    set;}

Result是唯一通知MVC架構當前Filter執行結果的媒介,也就是說MVC架構總是在必要的時候判斷filterContext.Result,如果Result不為空白就表示可以繼續,否則Result將被執行(因為它是個ActionResult),並且之後的過程將被跳過。在下面的討論中你會逐步理解。

 

 

IActionFilter和IResultFilter

IActionFilterIResultFilter分別表示在Action執行前動作和Action執行後動作。由前一篇的類圖,我們可以看到MVC內建了ActionFilterAttribute同時實現了這兩個介面,只是所有的實現都是虛方法,沒有任何實際的代碼。因此,可以通過繼承ActionFilterAttribute來實現IActionFilter和IResultFilter。除此之外,ActionFilterAttribute還繼承了FilterAttribute,這個Attribute只定義了一個Order屬性。事實上,對於多個相同的過濾器被定義在同一個action或controller上的時候,Order可以對他們的執行順序進行排序。如果沒有指定的話,預設的情況可以通過下面這個例子說明:

[ShowMessage(Message = "A")][ShowMessage(Message = "B")]public ActionResult SomeAction(){Response.Write("Action is running");return Content("Result is running");}

假設上面的ShowMessage繼承自ActionFilterAttribute,並實現了所有的四個方法,那麼將得到下面的輸出(省略了ShowMessage的實現,不過大家可以猜出來):

[BeforeAction B][BeforeAction A]Action is running[AfterAction A][AfterAction B]

[BeforeResult B][BeforeResult A]Result is running[AfterResult A][AfterResult B]

如果加上Order的話,可以改變這種預設的順序:

[ShowMessage(Message = "A", Order = 1)][ShowMessage(Message = "B", Order = 2)]public ActionResult SomeAction(){Response.Write("Action is running");return Content("Result is running");}

輸出:

[BeforeAction A][BeforeAction B]Action is running[AfterAction B][AfterAction A]

[BeforeResult A][BeforeResult B]Result is running[AfterResult B][AfterResult A]

總之,IActionFilter和IResultFilter還是比較容易理解的,但是有個特殊的問題需要注意,如果在執行IActionFilterIResultFilter的代碼時異常了怎麼辦?拿IActionFilter作說明,書中有這樣一張圖:

 

這張圖給了我們這樣的資訊,一個Action上面加了3層IActionFilter,當第三層的OnActionExecuting拋出異常後,被第二層的OnActionExecuted捕獲了,而且繼續執行第一層的OnActionExecuted。其中跳過了ActionMethod和第三層的OnActionExecuted。

IResultFilter實際上跟IActionFilter的行為完全相同。

另外,Response.Redirect()方法將拋出ThreadAbortException異常,MVC自行捕獲了這種特殊的異常,使得這種異常實際上不會影響我們,我們大可假裝對此完全不知。下面的代碼和注釋是從InvokeActionMethodFilter方法中截取的,說明了MVC架構在這裡的“用心良苦”。

catch (ThreadAbortException) {    // This type of exception occurs as a result of Response.Redirect(), but we special-case so that    // the filters don't see this as an error.    postContext = new ActionExecutedContext(preContext, preContext.ActionDescriptor, false /* canceled */, null /* exception */);    filter.OnActionExecuted(postContext);    throw;}

這部分的原始碼有個十分精闢的地方,我將寫一篇文章專門解讀這部分源碼,屆時,上述邏輯將更容易理解。

事實上,OutputCacheAttribute是一個IResultFilter,除了一些屬性,它僅僅重寫了OnResultExecuting,關於如何它的詳細使用,參考書中341頁。

 

IAuthorizationFilter

IAuthorizationFilter用於頁面層級的使用者驗證,AuthorizeAttribute實現了IAuthorizationFilter,下面的代碼是AuthorizeAttribute的核心驗證邏輯,需要通知滿足三個條件才能認證成功:

1、HttpContext.User.Identity.IsAuthenticated必須為true

2、使用者名稱必須一致(注意StringComparer.OrdinalIgnoreCase說明了不區分大小寫)

3、角色必須一致

protected virtual bool AuthorizeCore(HttpContextBase httpContext) {if (httpContext == null) {                throw new ArgumentNullException("httpContext");            }            IPrincipal user = httpContext.User;            if (!user.Identity.IsAuthenticated) {               return false;            }            if (_usersSplit.Length > 0 && !_usersSplit.Contains(user.Identity.Name, StringComparer.OrdinalIgnoreCase)) {                return false;            }            if (_rolesSplit.Length > 0 && !_rolesSplit.Any(user.IsInRole)) {                return false;            }            return true;}

上述原始碼十分清晰的說明了問題。另外,要實現角色驗證,需要在web.config中配置一個roleManager,通常可以使用SqlRoleProvider,也可以自訂。

如果考慮到Output Caching,Authorization Filters有什麼tricky嗎?我們看到OnAuthorization中的一段代碼:

            if (AuthorizeCore(filterContext.HttpContext)) {                // ** IMPORTANT **                // Since we're performing authorization at the action level, the authorization code runs                // after the output caching module. In the worst case this could allow an authorized user                // to cause the page to be cached, then an unauthorized user would later be served the                // cached page. We work around this by telling proxies not to cache the sensitive page,                // then we hook our custom authorization code into the caching mechanism so that we have                // the final say on whether a page should be served from the cache.                HttpCachePolicyBase cachePolicy = filterContext.HttpContext.Response.Cache;                cachePolicy.SetProxyMaxAge(new TimeSpan(0));                cachePolicy.AddValidationCallback(CacheValidateHandler, null /* data */);            }

這段代碼本沒什麼出奇的地方,先是調用AuthorizeCore,接著看到這段注釋,大意是:我們把驗證放到了Action部分,使得驗證代碼將在緩衝模組後面執行。考慮到最壞的情況,一個認證的使用者得到了一個敏感的頁面,並且這個頁面被緩衝了,接著,一個未驗證的使用者將得到快取頁面面而不需驗證。我們繞過了這個問題,直接告訴代理不要緩衝敏感頁面,並且把我們的驗證機制注入到緩衝機制中,使我們最終決定是否返回快取頁面面。

這段注釋清楚的說明了驗證機制和緩衝機制的矛盾,也給出瞭解決方案,因此,如果要自己實現IAuthorizationFilter一定要小心了,一定盡量繼承AuthorizeAttribute並僅重寫AuthorizeCore。如果還不明白看看下面回呼函數CacheValidateHandler最終調用的OnCacheAuthorization的實現就知道了:

        protected virtual HttpValidationStatus OnCacheAuthorization(HttpContextBase httpContext) {            if (httpContext == null) {                throw new ArgumentNullException("httpContext");            }            bool isAuthorized = AuthorizeCore(httpContext);            return (isAuthorized) ? HttpValidationStatus.Valid : HttpValidationStatus.IgnoreThisRequest;        }

在輸出緩衝之前仍然先調用AuthorizeCore,這樣就避免了上面注釋中提到的問題。

如果AuthorizeAttribute認證失敗,將構造一個HttpUnauthorizedResult並附給filterContext.Result。HttpUnauthorizedResult同樣繼承自ActionResult。其ExecuteResult方法如下:

context.HttpContext.Response.StatusCode = 401;

簡單的返回一個401錯誤,表示未驗證錯誤,然後驗證模組根據web.config的配置進行下一步操作,通常是跳轉到一個登入頁面。如果不希望這樣,可以重寫AuthorizeAttribute的HandleUnauthorizedRequest方法。比如在一個Ajax請求因為驗證錯誤而拒絕,你顯然不希望頁面跳轉。可以像下面這樣處理:

protected override void HandleUnauthorizedRequest(AuthorizationContext context){if (context.HttpContext.Request.IsAjaxRequest()) {UrlHelper urlHelper = new UrlHelper(context.RequestContext);context.Result = new JsonResult {Data = new {Error = "NotAuthorized",LogOnUrl = urlHelper.Action("LogOn", "Account")},JsonRequestBehavior = JsonRequestBehavior.AllowGet};}elsebase.HandleUnauthorizedRequest(context);}

 

IExceptionFilter

從前面一節的虛擬碼中可以看到,IExceptionFilter被設計成能夠捕獲異常。然而需要注意:僅僅從action開始執行之後的異常可以用這種方式捕獲(包括過濾器執行期間),在這之前的,諸如找不到controller,找不到action之類的異常是無法用IExceptionFilter捕獲的。

MVC內建了一個HandleErrorAttribute,它的作用是在捕獲異常後注入500錯誤(對於404錯誤將不處理)。來看看其內部對filterContext.Result的處理:

filterContext.Result = new ViewResult {ViewName = View,MasterName = Master,ViewData = new ViewDataDictionary<HandleErrorInfo>(model),TempData = filterContext.Controller.TempData};

注意到使用者指定的View、Master會被返回,還會有一個HandleErrorInfo的Model被封裝成ViewData返回,還會附帶上當前Controller的TempData。HandleErrorInfo封裝了Exception對象,Controller和Action的名字。這些資訊可以在我們的錯誤頁面中使用。filterContext.Result會被MVC架構執行,所以我們可以用一個非ViewResult指定,比如RedirectToRouteResult。

ControllerActionInvoker在執行filterContext.Result之前會判斷一下filterContext.ExceptionHandled是否為true,如果不為true,filterContext.Result將不會執行,那麼該死的黃頁還是會拋向ASP.NET。HandleErrorAttribute將檢查ExceptionHandled,如果為true則什麼都不做返回,否則將ExceptionHandled置為true。當我們需要自己實現IExceptionFilter,在同時有多個IExceptionFilter的時候,可以通過ExceptionHandled通知後面執行的IExceptionFilter異常是否被處理了。還需要注意的是:上面提到IActionFilter 也可以處理異常,可以猜到ActionExecutedContextResultExecutedContext也具有ExceptionHandled,對應的,如果在OnActionExecuted和OnResultExecuted中將ExceptionHandled設成了true,MVC架構將不會重新拋出異常,於是任何一個IExceptionFilter將沒有機會執行。

 

Controller自身實現過濾

Controller自身繼承自上述的四個介面,並且允許其繼承類覆蓋實現,所以我們也可以通過重寫OnActionExecuting等方法,為Controller設定過濾,這種過濾將優先於用屬性的方式設定的過濾執行。

public abstract class Controller : IActionFilter, IAuthorizationFilter,  IExceptionFilter, IResultFilter

勞動果實,轉載請註明出處:http://www.cnblogs.com/P_Chou/archive/2010/12/07/details-asp-net-mvc-08.html

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.