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屬性設定一個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: