16
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

ASP.NETAdvent Calendar 2016

Day 15

ASP.NET MVCのフィルターを触ってみた

Last updated at Posted at 2016-12-15

この記事はASP.NET Advent Calendar 2016の15日目の記事です。

ASP.NET MVCのFilterについて簡単にまとめてみます。

Filterの種類

承認フィルター

IAuthorizationFilterを実装
セキュリティ的にアクションを実行できるかの判断処理。
他のフィルターより前に実行される。
OnAuthorization・・・承認メソッド
ValidateAntiForgeryTokenAttributeが有名か。

アクションフィルター

IActionFilterを実装
アクションの前後に行いたい処理。
OnActionExecuting・・・アクション前に呼び出されるメソッド
OnActionExecuted・・・アクション後に呼び出されるメソッド

結果フィルター

IResultFilterを実装
ActionResultオブジェクトの実行前後に行いたい処理。
OnResultExecuting・・・ActionResultオブジェクト実行前に呼び出されるメソッド
OnResultExecuted・・・ActionResultオブジェクト実行後に呼び出されるメソッド

例外フィルター

IExceptionFilterを実装
MVC内でスローされた未処理の例外がある場合に呼び出される処理。
OnException・・・例外発生時に呼び出されるメソッド

MyFilterを作ってみた

ここからは独自実装の話になります。

通常、MVCを使ってUA判定でビューを切り替える場合はDisplayModesが使われると思いますが、
UA判定条件をカスタマイズしたかった為、Filterを使って実装してみました。

デバイス種別を持った薄いControllerラッパー
public class MyControllerBase : Controller
{
    // デバイス種別
    public enum DeviceType : byte
    {
        // PC
        PC = 0,
        // スマートフォン
        SmartPhone = 1,
    }
    public DeviceType TargetDevice { get; set; }
}
デバイス判定用ActionFilter属性クラス
public class DeviceJudgmentAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        // MyControllerBaseを継承しているControllerクラスであることが前提
        MvcControllerBase ctr = filterContext.Controller as MyControllerBase ;
        if (ctr != null)
        {
            // Requestを渡してデバイス種別を受け取る
            ctr.TargetDeviceType = GetDeviceType(filterContext.HttpContext.Request);
        }
        base.OnActionExecuting(filterContext);
    }
  
    // デバイス判定処理
    private DeviceType GetDeviceType(HttpRequestBase request)
    {
        // 判定Logicは省略
    }
}

あとは、このフィルターをMyControllerBaseを継承したControllerの好きなところに
属性として指定してあげれば完了です。

OnActionExecutingでアクション実行前にデバイス種別が取得出来ているので、
あとはその後のロジックで有効に活用してあげましょう。

アクションフィルターと承認フィルター

上記のアクションフィルターを実装してUA毎のページ切り分けを行っていたのですが、
実装後しばらくしてから問題があることに気付きました。

問題というのは 「例外フィルターで表示するエラーページをデバイス毎に切り分け」 です。

通常であれば、アクション実行前にフィルターが動いてUA判定を行ってくれるのでいいのですが、
アクション実行前に動いてしまうものがあります。

そうです。 「承認フィルター」 です。

承認処理は全てのフィルターの最初に実行され、承認が通らなかった場合は例外をスローします。

スローされた例外を基本的に例外フィルターで拾ってエラーページを表示していたのですが、
アクションフィルター実行前なのでUA判定が行われておらず、期待したページが表示されなかったのです。

ActionInvokerに無理矢理組み込む

ってことで、各フィルターを呼び出しているActionInvokerを無理矢理拡張しました。

デバイス判定処理を追加したActionInvokerクラス
public class DeviceJudgmentActionInvoker : ControllerActionInvoker
{
    // アクションを呼び出す処理をオーバーライドし、デバイス判定処理を先に行う
    public override bool InvokeAction(ControllerContext controllerContext, string actionName)
    {
        // MyControllerBaseを継承しているControllerクラスであることが前提
        MvcControllerBase ctr = filterContext.Controller as MyControllerBase ;
        if (ctr != null)
        {
            // Requestを渡してデバイス種別を受け取る
            ctr.TargetDeviceType = GetDeviceType(filterContext.HttpContext.Request);
        }
        return base.InvokeAction(controllerContext, actionName);
    }

    // デバイス判定処理
    private DeviceType GetDeviceType(HttpRequestBase request)
    {
        // 判定Logicは省略
    }
}
ControllerでActionInvokerの読み込み
public class MyController : MyControllerBase
{
    public MyController()
    {
        // コンストラクタ内でControllerの持つActionInvokerプロパティに、上記ActionInvokerインスタンスを設定
        ActionInvoker = new SetTargetDeviceActionInvoker();
    }
}

この実装で、承認フィルター前にデバイス判定を行うことが出来ました。

ActionInvoker拡張の注意点

上記の方法はフィルター実装前での処理になりますので、
これらの処理の中で例外が発生した場合は例外フィルターで拾えません。

16
16
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?