mvc原始碼解讀(12)-mvc四大過濾器之ActionFilter

風靈使發表於2018-07-10

上一篇中我們隨便聊了聊MVC的授權過濾器AuthorizeFilter,其實真正關於.net平臺下的認證體系之複雜遠遠超出了我們的想像,對許可權的控制我們只能找到相對安全的做法,並不能從絕對上杜絕不安全的驗證,特別是對於.net下授權許可權的控制。有機會我們可以回過頭來深入的探討有關.net平臺下的許可權控制體系。這一篇我們繼續根據mvc3的原始碼來學習mvc的ActionFilter方法過濾器。

AuthorizeFilter原理一樣,所有要用在Action方法上的方法過濾器特性都必須繼承抽象類ActionFilterAttribute,該類是實現了不僅實現了介面FilterAttribute,還是實現了介面IActionFilter, IResultFilter,介面IActionFilter的成員如下:

 public interface IActionFilter
 {
        void OnActionExecuting(ActionExecutingContext filterContext);
        void OnActionExecuted(ActionExecutedContext filterContext);
}

OnActionExecuting在特性標註的方法執行之前執行的方法,同理OnActionExecuted是在特性標註的方法後執行的方法。介面IResultFilter我們等到下章來介紹。OnActionExecutingOnActionExecuted接收ActionExecutingContext類例項作為引數,ActionExecutingContext類的成員如下:

public virtual ActionDescriptor ActionDescriptor {get; set;}

public virtual IDictionary<string, object> ActionParameters {get; set; }

public ActionResult Result {get;set;}

看到這些成員我們該笑了,ActionDescriptor屬性我們前面已經介紹過了,通過這個屬性我們可以獲取執行的ActionNameControllerDescriptor,進而得到Controller的一切資訊。ActionParameters獲取Action方法執行的引數。ActionExecutingContext類繼承自類ControllerContext,該類有以下幾個重要的成員如下:

public virtual ControllerBase Controller 
{
    get;
    set;
}

public virtual HttpContextBase HttpContext 
{
    get 
    {
        if (_httpContext == null) 
        {
            _httpContext = (_requestContext != null) ? _requestContext.HttpContext : new EmptyHttpContext();
        }
        return _httpContext;
    }
    set 
    {
        _httpContext = value;
    }
}

public virtual RouteData RouteData 
{
    get 
    {
        if (_routeData == null) 
        {
            _routeData = (_requestContext != null) ? _requestContext.RouteData : new RouteData();
        }
        return _routeData;
    }
    set 
    {
        _routeData = value;
    }
}

public RequestContext RequestContext 
{
    get 
    {
        if (_requestContext == null) 
        {
            HttpContextBase httpContext = HttpContext ?? new EmptyHttpContext();
            RouteData routeData = RouteData ?? new RouteData();
            _requestContext = new RequestContext(httpContext, routeData);
        }
        return _requestContext;
    }
    set 
    {
        _requestContext = value;
    }
}

這裡面我們需要注意的是HttpContextRequestContext這兩個屬性。一個是封住了有關HTTP請求的資訊,一個是封裝了HttpContextRouteData的請求上下文。看了那麼多的微軟定義的成員,我們來一個demo來演示ActionFilter的流程。

1:新建一個mvc3的應用程式,並建立一個控制器:

public class MyActionFilterController : Controller 
{
    [MyTestActionFiter]        
                 public ActionResult Index() 
    {
        return View();
    }
    public ActionResult TestIndex() 
    {
        return View();
    }
}

兩個Action對應的檢視如下:

@{Layout = null; }

<!DOCTYPE html>

<html> <head><title>Index</title></head><body><div><p>這裡是測試的第一個頁面--Index</p>    

</div> </body> </html>
@{Layout = null; }

<!DOCTYPE html>

<html> <head><title>TestIndex</title></head><body><div><p>這裡是測試的第二個頁面--TestIndex</p>    

</div> /body></html>

緊接著我們建立一個自定義的方法過濾器MyTestActionFiter,該類繼承自ActionFilterAttribute,具體如下:

public class MyTestActionFiter : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            //在動作請求之前執行的方法。
            filterContext.Result = new RedirectResult("/MyActionFilter/TestIndex");
            base.OnActionExecuting(filterContext);
        }
    }

開啟F12,我們請求的是如下所示:
這裡寫圖片描述

相應標頭的資訊如下:
這裡寫圖片描述

我們可以知道我們請求Index方法的時候,由於我們在OnActionExecuting方法中將請求重定向(HTTP狀態碼:302)到了TestIndex方法,由此可以證明OnActionExecuting是在Action方法之前執行。我們再來看一個demo。我們將MyTestActionFilter的方法改進一下:

public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            //在動作請求之前執行的方法。
            //filterContext.Result = new RedirectResult("/MyActionFilter/TestIndex");
            filterContext.HttpContext.Response.Write("這是方法執行前的操作<br/>");
            base.OnActionExecuting(filterContext);
        }
       public override void OnActionExecuted(ActionExecutedContext filterContext)
        {
            filterContext.HttpContext.Response.Write("這是方法執行之後的操作");
            base.OnActionExecuted(filterContext);
        }

同時將MyActionFilter控制器改成如下:

public class MyActionFilterController : Controller
{
 [MyTestActionFiter]        
 public void Index() 
 {
 Response.Write("這裡是測試的第一個頁面--Index<br/>");
 }
}

按F5執行,執行結果如下:

這裡寫圖片描述

看到這裡,大家應該對ActionFiter的執行順序應該有個大概的瞭解了吧:OnActionExecuting>Action>OnActionExecuted。但是有一點需要注意:ActionFiter都是在ViewResult執行之前執行,這也是為什麼我在MyActionFilter中是直接輸出內容,而不是返回ViewResult的原因,關於這一點,我們下一章結合ResultFilter做一個詳細的解說。

相關文章