しばやん雑記

Azure とメイドさんが大好きなフリーランスのプログラマーのブログ

ASP.NET MVC 4 で現在の Display Mode を見てアクションを振り分ける

ASP.NET MVC 4 アプリケーションのスマートフォン対応は、理想的には ASP.NET MVC 4 というか Web Pages 2 で追加された Display Modes だけで解決できればいいんですが、今回はどうしてもアクションを分けたくなりました。

しかし、他のページはビューだけ切り替えている関係上、URL を別々のものにはしたくないです。そこで以前 ASP.NET MVC 4 で現在の Display Mode を取得する - しばやん雑記 に現在の Display Mode を取得するコードを書いていたので、これを利用して Display Mode でアクションを切り替える属性を作成してみました。

public class DisplayModeAttribute : ActionMethodSelectorAttribute
{
    public DisplayModeAttribute(string displayModeId)
    {
        _displayModeId = displayModeId;
    }

    private readonly string _displayModeId;

    public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo)
    {
        var displayMode = DisplayModeProvider.Instance.GetAvailableDisplayModesForContext(controllerContext.HttpContext, controllerContext.DisplayMode).FirstOrDefault();

        if (displayMode == null)
        {
            return true;
        }

        return displayMode.DisplayModeId == _displayModeId;
    }
}

ちなみに ActionMethodSelectorAttribute クラスを継承すれば、簡単にアクションメソッドを切り替える属性は作れます。今回はコンストラクタで Display Mode を追加する時に指定した識別子*1を受け取るようにして、一致する場合のみアクションが選択されるようにしています。

使い方は以下のような感じです。簡単ですね。

public ActionResult Index()
{
    ViewBag.Message = "Desktop";

    return View();
}

[DisplayMode("Mobile")]
public ActionResult Index(string userAgent)
{
    ViewBag.Message = "Display";

    return View();
}

DisplayMode 属性を付けたアクションに userAgent という引数を追加しているのは、そのままだとオーバーロードメソッドを定義出来ないという言語側の制限です。なので、特にこの引数に意味はありません。

実行してみると、ちゃんと Display Mode の設定によって実行されたアクションが振り分けられているのが分かります。

f:id:shiba-yan:20130521135457p:plain

この状態で Index.Mobile.cshtml というファイルを追加すると、同一の URL で完全にアクションとビューを分離することが出来ますね。

スマートフォンに対応する場合には便利に使えそうなので、GitHub のプロジェクトにもコードを追加しておくことにします。

*1:DefaultDisplayMode のコンストラクタで指定するサフィックスのこと