2013年6月9日 星期日

[Asp.net MVC] Custom Action Filter and Trace

上一篇,已經針對 Action Filter 作了一個初步的介紹,這篇就來試著實作自己的Action Filter。

Action Filter 是實作 一個繼承 ActionFilterAttribute 的一個 Attribute 類別, ActionFilterAttribute 是一個抽象類別,包含四個 virtual methods : OnActionExecuting、OnActionExecuted、OnResultExecuting、OnResultExecuted。

這邊是MSDN的原文說明,相信會讓大家有更清楚的認知:

An action filter is implemented as an attribute class that inherits from ActionFilterAttribute. ActionFilterAttribute is an abstract class that has four virtual methods that you can override: OnActionExecuting, OnActionExecuted, OnResultExecuting, and OnResultExecuted. To implement an action filter, you must override at least one of these methods.

The ASP.NET MVC framework will call the OnActionExecuting method of your action filter before it calls any action method that is marked with your action filter attribute. Similarly, the framework will call the OnActionExecuted method after the action method has finished.

The OnResultExecuting method is called just before the ActionResult instance that is returned by your action is invoked. The OnResultExecuted method is called just after the result is executed. These methods are useful for performing actions such as logging, output caching, and so forth.

 

首先我們先建立一個 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讓它動起來,觀察執行的先後順序是否如同上面所描述的一樣  :


sdsd


 


那如果 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之後,我們會得到以下的結果


sdx


從上面的結果我們可以發現 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);
}

 


我們會得到以下的結果 :


sss


可以發現,當Trace 的 OnActionExecuting 事件被中止,尚未執行的Log OnActionExecuting 也一樣被中止,直接進入OnResultExecuting 繼續執行,由此可知 OnActionExecuting 和 OnResultExecuting 兩個是獨立的流程,並不會因為 OnActionExecuting 發生Cancel 而影響 OnResultExecuting 。


 


參考資料 :


MSDN -- Filtering in ASP.NET MVC

沒有留言:

張貼留言