上一篇,已經針對 Action Filter 作了一個初步的介紹,這篇就來試著實作自己的Action Filter。
Action Filter 是實作 一個繼承 ActionFilterAttribute 的一個 Attribute 類別, ActionFilterAttribute 是一個抽象類別,包含四個 virtual methods : OnActionExecuting、OnActionExecuted、OnResultExecuting、OnResultExecuted。
這邊是MSDN的原文說明,相信會讓大家有更清楚的認知:
首先我們先建立一個 MVC 專案,並新增一個Filter資料夾,然後在其中新增一個繼承 ActionFilterAttribute 的 Trace class ,並 override ActionFilterAttribute 的四個 virtual methods :
public class Trace : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
Debug.WriteLine("OnActionExecuting");
base.OnActionExecuting(filterContext);
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
Debug.WriteLine("OnActionExecuted");
base.OnActionExecuted(filterContext);
}
public override void OnResultExecuting(ResultExecutingContext filterContext)
{
Debug.WriteLine("OnResultExecuting");
base.OnResultExecuting(filterContext);
}
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
Debug.WriteLine("OnResultExecuted");
base.OnResultExecuted(filterContext);
}
}
接著,按下F5讓它動起來,觀察執行的先後順序是否如同上面所描述的一樣 :
那如果 Action Filter 套用多個呢? 首先我們再新增一個 ActionFilter (Log) :
public class Trace : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
Debug.WriteLine("OnActionExecuting from Trace");
base.OnActionExecuting(filterContext);
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
Debug.WriteLine("OnActionExecuted from Trace");
base.OnActionExecuted(filterContext);
}
public override void OnResultExecuting(ResultExecutingContext filterContext)
{
Debug.WriteLine("OnResultExecuting from Trace");
base.OnResultExecuting(filterContext);
}
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
Debug.WriteLine("OnResultExecuted from Trace");
base.OnResultExecuted(filterContext);
}
}
public class Log : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
Debug.WriteLine("OnActionExecuting from Log");
base.OnActionExecuting(filterContext);
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
Debug.WriteLine("OnActionExecuted from Log");
base.OnActionExecuted(filterContext);
}
public override void OnResultExecuting(ResultExecutingContext filterContext)
{
Debug.WriteLine("OnResultExecuting from Log");
base.OnResultExecuting(filterContext);
}
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
Debug.WriteLine("OnResultExecuted from Log");
base.OnResultExecuted(filterContext);
}
}
將 Trace、Log 兩個 Filter 都套用到 Action之後,我們會得到以下的結果
從上面的結果我們可以發現 Action Filter 中的 Executing 是由上而下執行,而 Executed 是由下而上執行,類似堆疊 "後進先出"的概念。
ActionExecuting、ResultExcuting 執行階段是可以被終止、Cancel 的(例如當發生execption時),所以我們再把 Trace 這個 Filter 修改一下,使其拋出一個 non-null Result,來模擬這個狀況
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
Debug.WriteLine("OnActionExecuting from Trace");
//加入下面這一行,讓result 為 non-null
//也就是 filterContext.RouteData.Values.ContainsValue("Cancel") = true
filterContext.Result = new EmptyResult();
base.OnActionExecuting(filterContext);
}
我們會得到以下的結果 :
可以發現,當Trace 的 OnActionExecuting 事件被中止,尚未執行的Log OnActionExecuting 也一樣被中止,直接進入OnResultExecuting 繼續執行,由此可知 OnActionExecuting 和 OnResultExecuting 兩個是獨立的流程,並不會因為 OnActionExecuting 發生Cancel 而影響 OnResultExecuting 。
參考資料 :