ASP.NET MVCについてもチマチマと、とりあえず基本のあたり(3)
VCのCの拡張ってIControllerFactoryの拡張ポイントの話で、コントローラ自体の拡張ポイントの話が抜けていたので、フィルタについて書いてみます(・∀・)
フィルタ
ASP.NET MVCには以下の4つのインタフェースが存在し、これらの実装クラスを用意することで、コントローラの処理ステージの前後に、独自の処理を挟み込む事が可能になっています(・∀・)
- IAuthorizationFilter 認証処理というか、前処理用。
- IActionFilter コントローラのアクションメソッドの実行前後処理用。
- IResultFilter ActionResultの実行前後処理用。
- IExceptionFilter 例外発生時の処理用。
また、これらのフィルタが呼び出される順番は、通常は(フィルタの途中でActionResultを設定したりしなければ)下記のよう順になります。
- IAuthorizationFilter.OnAuthorization()
- IActionFilter.OnActionExecuting()
- コントローラのアクションメソッド実行
- IActionFilter.OnActionExecuted()
- IResultFilter.OnResultExecuting()
- ActionResult.ExecuteResult()
- IResultFilter.OnResultExecuted()
- IExceptionFilter.OnException() (例外発生時のみ)
なお、標準で用意されている実装としては、OutputCache(出力キャッシュ)、HandleError(エラーハンドリング)、Authorize(IPrincipalによる認証)等が存在します。
独自フィルタの作成
独自のフィルタを作成する場合、上記いずれかのインタフェースを実装したFilterAttribute派生クラスを作成するか、FilterAttributeを派生してIActionFilter、IResultFilterを実装したActionFilterAttributeクラスが標準で用意されているので、ActionFilterAttribute派生クラスを作成することになります。
FilterAttribute派生クラスを作成してコントローラクラスに属性を付与すると、フレームワーク内でそのフィルタインタフェースの処理を呼び出してくれるようになっています。
試しに、4つのインタフェース全てを実装し、処理の各段でデバッグ出力を行う下記のようなFilterAttribute派生クラスを作成してみます(・∀・)
[AttributeUsage( AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false )] public class CustomFilterAttribute : FilterAttribute, IAuthorizationFilter, IActionFilter, IResultFilter, IExceptionFilter { public void OnAuthorization(AuthorizationContext filterContext) { Trace.WriteLine( "Filter:OnAuthorization()" ); } public void OnActionExecuting(ActionExecutingContext filterContext) { Trace.WriteLine( "Filter:OnActionExecuting()" ); } public void OnActionExecuted(ActionExecutedContext filterContext) { Trace.WriteLine( "Filter:OnActionExecuted()" ); } public void OnResultExecuting(ResultExecutingContext filterContext) { Trace.WriteLine( "Filter:OnResultExecuting()" ); } public void OnResultExecuted(ResultExecutedContext filterContext) { Trace.WriteLine( "Filter:OnResultExecuted()" ); } public void OnException(ExceptionContext filterContext) { Trace.WriteLine( "Filter:OnException()" ); } }
次にCustomFilterAttributeを適用するコントローラクラスとして以下のようなものを用意します。
Controllerクラスも4つのインタフェースを実装しており、コントローラ固有のフィルタ処理であればメソッドをoverrideすることによりそこに処理を書くことも可能です。
[CustomFilter] public class FilterController : Controller { public ActionResult Index() { return View(); } protected override void OnAuthorization(AuthorizationContext filterContext) { Trace.WriteLine( "Controller:OnAuthorization()" ); } protected override void OnActionExecuting(ActionExecutingContext filterContext) { Trace.WriteLine( "Controller:OnActionExecuting()" ); } protected override void OnActionExecuted(ActionExecutedContext filterContext) { Trace.WriteLine( "Controller:OnActionExecuted()" ); } protected override void OnResultExecuting(ResultExecutingContext filterContext) { Trace.WriteLine( "Controller:OnResultExecuting()" ); } protected override void OnResultExecuted(ResultExecutedContext filterContext) { Trace.WriteLine( "Controller:OnResultExecuted()" ); } protected override void OnException(ExceptionContext filterContext) { Trace.WriteLine( "Controller:OnException()" ); } }
っで、上記コントローラを実行すると、各フィルタの実行順序が下記のように確認できます。
Controller:OnAuthorization() Filter:OnAuthorization() Controller:OnActionExecuting() Filter:OnActionExecuting() Filter:OnActionExecuted() Controller:OnActionExecuted() Controller:OnResultExecuting() Filter:OnResultExecuting() Filter:OnResultExecuted() Controller:OnResultExecuted()
なお、複数のフィルタを適用する場合の適用順序についてですが、FilterAttributeはOrderプロパティを持っており、その値を指定することにより制御が可能になっています(`・ω・´)
…ってなカンジ(・∀・)
IControllerFactoryとIViewEngine以外の話はまとめて書こうと思っていたけれど、フィルタの話が長くなったので独立させまスタ(´ω`)