ActionFilter的流程以及ActionFilter流程結束時間點

ActionFilter的流程以及ActionFilter流程結束時間點的研究。

base.OnActionExecuting(filterContext)的用途

不加上base.OnActionExecuting的情況

範例程式:

FilterBaseAttribute.cs:

namespace ActionFilterLab.API.Filter
{
    public class FilterBaseAttribute: ActionFilterAttribute
    {
     
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {          
        	//base.OnActionExecuting(filterContext);     可以不需要這行
            filterContext.HttpContext.Response.WriteAsync(string.Format("{0}.OnActionExecuting()<br/>", this.GetType().Name));            
        }
    }
}

ApiController.cs:


        [HttpGet]
        [FilterBase]
        public IActionResult Get()
        {
            return Ok();
        }

上面範例可以看到,FilterBaseAttribute是直接繼承自ActionFilterAttribute,並覆寫OnActionExecuting。這種情形下,就算加上了base.OnActionExecuting(filterContext);,由於ActionFilterAttribute.OnActionExecuting裡面是空的,所以就算沒有加上base.OnActionExecuting(filterContext);也不會有任何影響。

 

必須要加上base.OnActionExecuting的情況

範例程式:

FilterBaseAttribute.cs:

namespace ActionFilterLab.API.Filter
{
    public abstract class FilterBaseAttribute: ActionFilterAttribute
    {
        public override void OnActionExecuted(ActionExecutedContext filterContext)
        {
            filterContext.HttpContext.Response.WriteAsync(string.Format("{0}.OnActionExecuted()<br/>", this.GetType().Name));
        }        
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {         
            filterContext.HttpContext.Response.WriteAsync(string.Format("{0}.OnActionExecuting()<br/>", this.GetType().Name));
        }
    }

    public class FooAttribute : FilterBaseAttribute
    {
    
    }

    public class BarAttribute : FilterBaseAttribute
    {
       public override void OnActionExecuting(ActionExecutingContext filterContext)
       {           
           filterContext.Result = new EmptyResult();
           base.OnActionExecuting(filterContext);
       }
    }

    public class BazAttribute : FilterBaseAttribute
    { 
    
    }
}

ApiController.cs:


        [HttpGet]
		[Bar]
        public IActionResult Get()
        {
            return Ok();
        }

上面範例可以看到,Filter Attribute(Foo. Bar. Baz)不是直接繼承ActionFilterAttribute然後覆寫(override) OnActionExecuting,而是又有自己的覆寫(override)方法。這種情況下,如果又沒加上base.OnActionExecuting(filterContext);的話,那就無法執行到Response訊息的那一行程式了。

由以上範例程式可以看出,其實加不加base.OnActionExecuting(filterContext);並沒有一定的答案,主要是看自己的程式邏輯。

另外,藉由這個範例也可以複習一下物件導向(abstract. virtual. override)的觀念。


ActionFilter的流程

由上圖可以得知,如果有套用多個ActionFilter的話,他的執行流程會如上圖。但這邊有一個地方要注意。如果我們return的地方不是在Action Method,而且我們在覆寫的OnActionExecuting跟OnActionExecuted裡面也沒有設定Result屬性的話,這樣可是會回傳錯誤的,因為整個流程沒有回傳值。

上面的敘述很難看懂,我們來看看範例程式碼:

FilterBaseAttribute.cs:

namespace ActionFilterLab.API.Filter
{
    public abstract class FilterBaseAttribute: ActionFilterAttribute
    {
        public override void OnActionExecuted(ActionExecutedContext filterContext)
        {
            filterContext.HttpContext.Response.WriteAsync(string.Format("{0}.OnActionExecuted()<br/>", this.GetType().Name));
        }
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {         
            filterContext.HttpContext.Response.WriteAsync(string.Format("{0}.OnActionExecuting()<br/>", this.GetType().Name));
        }
    }

    public class FooAttribute : FilterBaseAttribute
    {

    }

    public class BarAttribute : FilterBaseAttribute
    {

    }

    public class BazAttribute : FilterBaseAttribute
    {

    }
}

ApiController.cs:

        [HttpGet]
        [Foo(Order =1)]
        [Bar(Order = 2)]
        [Baz(Order = 3)]
        public IActionResult Get()
        {
            return NotFound();
        }

質性順序流程會如下圖,整個Action Filter的最後一個結束點是在FooAttribute.OnActionExecuted。

如果我們在FooAttribute.OnActionExecuted裡面沒有設定Result的屬性值的話,就會因為整個流程沒有回傳值而回應錯誤。

但如果我們覆寫FooAttribute.OnActionExecuted,再把Result屬性設定一個EmptyResult的話,整個流程因為有了回傳值,就能順利的結束了。真的是可喜可賀!

指定Action Filter流程的結束點

假如我們想在BarAttribute.OnActionExecuting來結束整個Action Filter流程,就在BarAttribute.OnActionExecuting設定Result值。

namespace ActionFilterLab.API.Filter
{
    public abstract class FilterBaseAttribute: ActionFilterAttribute
    {
        public override void OnActionExecuted(ActionExecutedContext filterContext)
        {
            filterContext.HttpContext.Response.WriteAsync(string.Format("{0}.OnActionExecuted()<br/>", this.GetType().Name));
        }
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {         
            filterContext.HttpContext.Response.WriteAsync(string.Format("{0}.OnActionExecuting()<br/>", this.GetType().Name));
        }
    }

    public class FooAttribute : FilterBaseAttribute
    {

    }

    public class BarAttribute : FilterBaseAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext filterContext)
       {
           base.OnActionExecuting(filterContext);
           filterContext.Result = new EmptyResult();
        }
    }

    public class BazAttribute : FilterBaseAttribute
    {

    }
}
覆寫(override)的好處

這裡我們也可以看到override的好處,透過覆寫(override)我們就可以在我們指定要結束的流程點設定Result的值,來結束整個Action Filter流程。

如果我們在FilterBaseAttribute.OnActionExecuting設定Result。這樣就會變成在第一個流程點FooAttribute.OnActionExecuting就設定了Result值,而導致整個流程在這裡就結束了。

namespace ActionFilterLab.API.Filter
{
    public abstract class FilterBaseAttribute: ActionFilterAttribute
    {
        public override void OnActionExecuted(ActionExecutedContext filterContext)
        {
            filterContext.HttpContext.Response.WriteAsync(string.Format("{0}.OnActionExecuted()<br/>", this.GetType().Name));
        }
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {         
            filterContext.HttpContext.Response.WriteAsync(string.Format("{0}.OnActionExecuting()<br/>", this.GetType().Name));
            filterContext.Result = new EmptyResult();//在這裡設定Result,會導致Action Filter流程在FooAttribute.OnActionExecuting就結束了。
        }
    }

    public class FooAttribute : FilterBaseAttribute
    {

    }

    public class BarAttribute : FilterBaseAttribute
    {

       //public override void OnActionExecuting(ActionExecutingContext filterContext)
       //{
       //  base.OnActionExecuting(filterContext);
       //  filterContext.Result = new EmptyResult();
       //}
    }

    public class BazAttribute : FilterBaseAttribute
    {

    }
}

瞭解ActionFilter後再搭配ExceptionFilter,以後就可以做一些log 事件的捕捉了,以後有空再來研究研究。

 

Ref: