ASP.NET MVC コア拡張部
One ASP.NET Advent Calendar 2012 の 9 日の記事です。
前日の 8 日は ASP.NET MVC と WF4 を一緒に使う際に気をつけるたった一つのこと
後日は 10 日は WebFormっぽいコントロールベスト3こちらになります。
ASP.NET MVC は拡張性に優れており、あらゆるところで拡張が可能になっています。
その中で、一番フレームワークのコアに近いところである、コア拡張部について、
拡張することで何ができるか?を中心に紹介したいと思います。
めったに利用しない
これから紹介する拡張は、1アプリケーションに対して1回あるかないか…、
もしかしたら人生において、1回も体験することはないかもしれません。
が、万が一アプリケーション特有のなにか、が必要になった時、
これらの拡張が役立つことは十分にありえるかと思います。
目次
紹介する部分は以下の通りです。
- 1.コントローラファクトリー ControllerFactory
- 2.アクションインボーカー ActionInvoker
- 3.モデルバインダー ModelBinder
- 4.バリュープロバイダー ValueProvider
- 5.モデルバインダープロバイダー ModelBinderProvider
- 6.モデルメタデータプロバイダー ModelMetaDataProvider
- 7.モデルバリデータプロバイダー ModelValidatorProvider
- 8.ビューエンジン ViewEngine
- 9.テンプデータプロバイダー TempDataProvider
1.コントローラファクトリー ControllerFactory
なにをするところか?
コントローラのインスタンスを生成しているところです。
デフォルト(置き換える前の実装を担当しているクラス)
- DefaultControllerFactory
Controllers の名前配下に、〜〜Controller クラスを置くという決まりは、
このデフォルトの実装からきています。
拡張したら何ができるか?例えば:
- コントローラを取得できない場合、独自の処理実行することができます。
- コントローラを独自の namespace に置くことができます。
- コントローラクラスのコンストラクタの引数に、任意の値をいれることができます。
設定できる範囲
- アプリケーション全体
ControllerBuilder.Current.SetControllerFactory(new MyControllerFactory());
2.アクションインボーカー ActionInvoker
なにをするところか?
- アクションメソッドを選択し、実行します。
- アクションメソッドの前後で、アクションフィルターを実行し、最後には、ビューを呼び出します。
デフォルト
- ControllerActionInvoker
拡張したら何ができるか?例えば:
- アクションフィルターを WebConfig から動的に追加できます。
- ある条件の時にだけ、アクションフィルターを無効にできます。
- アクションフィルターとの結合テストのために、このアクションインボーカーを実装することもあります。
設定できる範囲
- コントローラクラス毎
アクションインボーカーは、コントローラクラスに付属しているので、
コントローラ毎に違うアクションインボーカーを設定できます。
public HomeController() { this.ActionInvoker = new MyActionInvoker(); }
3.モデルバインダー ModelBinder
なにをするところか?
- リクエストデータ等から、コントローラに渡すモデル(アクションメソッドの引数に定義しているモデル)を作成します。
- モデルを作成する時に、検証を行います。
デフォルト
HttpPostedFileBase や、byte[] のモデルバインダー等、複数あります。
拡張したら何ができるか?例えば:
- 独自のクラスを、アクションメソッドの引数のモデルに定義することができます。
- 年、月、日といった複数の日付のテキストボックスの値から、DateTime を作成して、アクションメソッドの引数に渡すことができます。参考サイト:Splitting DateTime - Unit Testing ASP.NET MVC Custom Model Binders
- 入力値の補正。ひらがなで入力された値を、カタカナに補正して、アクションメソッドの引数に渡すことができます。(全角→半角など!)
設定できる範囲
- アプリケーション全体
ModelBinders.Binders.DefaultBinder = new MyModelBinder();
- コントローラのアクションメソッド単位
public ActionResult Index([ModelBinder(typeof(MyModelBinder))] MyModel model) { return View(); }
- 型
この型は、このモデルバインダーを使って作成する、といった設定ができます。
ModelBinders.Binders.Add(typeof(MyModel), new MyModelBinder());
[ModelBinder(typeof(MyModelBinder))] public class MyModel { }
ひとこと
モデルバインダーの拡張はよく利用されているかもしれません。
モデルの作成と検証をカプセル化できるので、本当のモデルを定義してどんどん使いたいところです。
相棒
ASP.NET MVC のモデルバインダーの仕組みでは、このモデルバインダー以外にも登場人物がいます。
- モデルバインダープロバイダー
- どのモデル(型)の時に、どのモデルバインダーを使用するかを決めます。
- バリュープロバイダー
- モデルを作成するためには、材料(Cookieの値や、送信された値)が必要で、その材料を提供してくれます。
次はこの2つについてです。
4.バリュープロバイダー ValueProvider
なにをするところか?
- キー(文字列)から、リクエストの値を提供します。
先ほどのモデルバインダーへ値を提供するのが、このバリュープロバイダーです。
デフォルト
ルートデータ、フォーム送信値、等を提供するプロバイダーがあります。
拡張したら何ができるか?例えば:
- アクションメソッドの引数のモデルを、Cookie や TempData、Session 等の値を使って作成されるようにすることができます。
- ユーザ名を保存するかどうかのチェックを、Cookie から取得して、アクションメソッドの引数に渡されるようにできます。参考サイト:ASP.NET MVC Working Smart With Cookies ? Custom ValueProvider
- アクションメソッドの引数に、今ログインしているユーザ名が入るようにすることができます。参考サイト:How to create a custom Value Provider for MVC
設定できる範囲
- アプリケーション全体
ValueProviderFactories.Factories.Add(new MyValueProviderFactory());
補足
Cookie と TempData、Session などのバリュープロバイダーは既に ASP.NET MVC 3 Futures の中にあります。
5.モデルバインダープロバイダー
なにをするところか?
ある型を作成するためのモデルバインダーを動的に選択して提供します。
デフォルト
ありません。
モデルバインダーを選択する方法は、このプロバイダーを使う以外の方法があるので、
なくても問題ありません。
拡張したら何ができるか?例えば:
例えば、この型はこのモデルバインダー、あの型はこのモデルバインダー、と適用するには、
モデルバインダープロバインダーを使わずとも、3.モデルバインダーで記載したように
以下のように書くことで実現できます。
ModelBinders.Binders.Add(typeof(MyModel), new MyModelBinder());
が、上の場合、MyModel を継承した MyChildModel の場合は、MyModelBinder が適用されません。
ここで、モデルバインダープロバイダーの拡張の出番です。
↓のサイトにこの場合の実装例があります。
参考サイト:an inheritance-aware modelbinderprovider in mvc 3
適用範囲
- アプリケーション全体
ModelBinderProviders.BinderProviders.Add(new MyModelBinderProvider);
6.モデルメタデータプロバイダー ModelMetaDataProvider
何をするところか?
- モデルメタデータを提供します。
モデルメタデータとは、モデルの付随情報のことで、表示名、テンプレート名、必須かどうか、などの情報が入っています。
これらのモデルメタデータは、どんな時に利用されるかというと、
ビューの表示の時や、モデルバインダ時(検証時も含む)等、フレームワークの様々な所で利用されます。
(@Html.LaborFor は、表示名を参照、検証時は必須かどうかの情報を参照、といった風に。)
拡張したら何ができるか?例えば:
- モデルメタデータに埋め込む情報を自分で定義し、ビューや検証で利用することができます。
- テキストボックスの maxlength や、onkeyup 等を指定する属性を作り、ビューでその値を出力することができます。 参考サイト:inputタグに属性を埋め込むメタデータクラス用属性作成
- こちらでは、ToolTip という属性を使っています。参考サイト:Creating your own modelmetadataprovider to handle custom attributes
- ReadOnly 属性が付いた項目は、表示用のテンプレートが使用されるようにすることができます。
- WebConfig などから動的に、項目に Required 属性などの属性を追加することができます。
適用範囲
- アプリケーション全体
ModelMetadataProviders.Current = new MyModelMetadataProvider();
7.モデルバリデータプロバイダー ModelValidatorProvider
何をするところか?
モデルを検証するモデルバリデータを提供します。
デフォルト
検証属性を使ったバリデータや、IDataErrorInfo を使ったバリデータ、数値のバリデータ(クライアント)
を提供します。
拡張したら何ができるか?例えば:
- 自分で作成したモデルバリデータが使用されるようにすることができます。
- 外部コンポーネントによる検証を行う、検証属性を作成して適用することができます。ValidationAttribute の検証メソッド内で、外部コンポーネントを利用する
適用範囲
- アプリケーション全体
ModelValidatorProviders.Providers.Add(new MyModelValidatorProvider());
8.ビューエンジン ViewEngine
何をするところか?
- ビューオブジェクトを検索し、作成します。
ビューは "Index" といった様に文字列で指定されるので、それに一致する
ビューオブジェクト(HTML レスポンスを書きだすもの)を作成します。
デフォルト
- WebFormViewEngine
- RazorViewEngine
ビューは、Views フォルダ配下・Shared フォルダ配下に置く、といった決まりは、
このデフォルトの実装によるものです。
拡張したら何ができるか?例えば:
- ビューである 〜.cshtml ファイルを、任意のフォルダ配下に置くことができます。
- ユーザのカルチャに合わせて、〜.ja.cshtml・〜.is.csHtml などのビューを返すことができます。
注意
ASP.NET MVC 4 には、ユーザ・エージェントに合わせてビューを切り替える
Display Modes という機能があるので、ビューエンジンを実装するよりも、
Display Modes を選択した方が良いです。
9.テンプデータプロバイダー TempDataProvider
何をするところか?
- 一時的な値を保持します。
デフォルト
- SessionStateTempDataProvider
セッション状態を利用して値を保持します。
拡張したら何ができるか?例えば:
- セッション状態が使えない場合に、別の方法で値を保持する、という手段をとることができます。
- セッション状態ではなく、Cookie を利用して値を保持することができます。
適用範囲
- コントローラ毎
public HomeController() { this.TempDataProvider = new MyTempDataProvider(); }
補足
Cookie を利用したテンプデータプロバイダーは、既に ASP.NET MVC 3 Futures の中にあります。
(CookieTempDataProvider )
おしまい
ASP.NET MVC 拡張の全貌については、こちらの記事 ASP.NET MVCパイプラインと拡張性
が参考になります。
拡張の入門記事としては、こちらの記事 An Introduction to ASP.NET MVC Extensibility
が紹介されています。この記事の右側にある、ASP.NET MVC Pipeline.pdf というファイルは
ASP.NET MVC フレームワークの理解にとても役立ちます。