MVC 3フレームワークアプリケーションでのライフサイクルのリクエスト

開発者は、URLがMVCルーティングで重要な役割を果たすことを知っています。 ご存知のように、標準的なルーティングがあります:



routes.MapRoute( "Default", "{controller}/{action}/{id}", new { controller = "Home", action = "Index", id = UrlParameter.Optional );
      
      







ルートを選択するルールです。 最も単純なケースでは、アクションとコントローラーを交換するか、次のようにURLを検索するパラメーターをさらに追加できます-param1 / param2 / param3 /。 たとえば、CMSシステムでは、url部分を変数として作成する必要がある場合があります。



 routes.MapRoute( "Default", "{controller}/{action}/{*url}", new { controller = "Content", action = "Home", url = UrlParameter.Optional });
      
      







このルールは、アクションとコントローラーを割り当て、残りをurl変数に割り当てます。

ルートは、アプリケーションの起動時に登録されます。 つまり、Application_Start()メソッドのGlobal.asaxファイル内。



大部分のアプリケーションを作成するには、この知識で十分です。 私の記事の目標は、リクエストのライフサイクル全体を見て、アプリケーションがURLを解析して制御を目的のアクションに移す前に何が起こるかを確認することです。



1. MVC要求ハンドラーを作成します。

MVCには、次の種類のハンドラーが含まれています。 (MSDNに基づく)

•MvcHandler。 このハンドラーは、MVCアプリケーションのASP.NETパイプラインを開始します。 MVCコントローラーファクトリーからコントローラーのインスタンスを受け取ります。 このコントローラーは、要求のさらなる処理を実行します。 MvcHandlerはIHttpHandlerを実装しますが、クラスはパラメーターなしのコンストラクターをサポートしないため、ハンドラーとして(たとえば、.mvcファイル名を展開するために)一致できません。 (必要なコンストラクターはRequestContextオブジェクトのみです)。

•MvcRouteHandler。 このクラスはIRouteHandlerを実装しているため、ASP.NETルーティングと統合できます。 MvcRouteHandlerクラスは、ルートをMvcHandlerインスタンスに関連付けます。 MvcRouteHandlerインスタンスは、MapRouteメソッドを使用してルーティングに登録されます。 MvcRouteHandlerクラスが呼び出されると、クラスは現在のRequestContextインスタンスを使用してMvcHandlerインスタンスを作成します。 次に、コントロールをMvcHandlerの新しいインスタンスに渡します。

•MvcHttpHandler。 このハンドラーは、ルーティングモジュールをバイパスして直接ハンドラーマッピングを簡素化するために使用されます。 これは、.mvcなどのファイル名拡張子をMVCハンドラーに直接マップする必要がある場合に便利です。 内部的に、MvcHttpHandlerは通常のASP.NETルーティングと同じタスクを実行します(MvcRouteHandlerおよびMvcHandlerを通過)。 ただし、これらのタスクはモジュールではなくハンドラーとして実行されます。 UrlRoutingModuleがすべての要求に対して有効になっている場合、このハンドラーは通常使用されません。



なぜこれが必要なのでしょうか?



MvcRouteHandlerオーバーライドの一般的なビューは次のとおりです。



 public class CustomRouteHandler : MvcRouteHandler { protected override IHttpHandler GetHttpHandler(RequestContext requestContext) { //TODO: something return base.GetHttpHandler(requestContext); } }
      
      







Application_Start()メソッドのグローバルクラスでCustomRouteHandlerを初期化する



 routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); var route = routes.MapRoute( "Default", // Route name "{controller}/{action}/{id}", // URL with parameters new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults ); route.RouteHandler = new CustomRouteHandler();
      
      







2.コントローラーの選択と作成

ControllrerFactoryクラスは、コントローラーの選択と作成を担当します。 このクラスは、IControllerFactoryインターフェイスを実装します。



  public interface IControllerFactory { IController CreateController(RequestContext requestContext, string controllerName); SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName); void ReleaseController(IController controller); }
      
      







当然、このインターフェイスを実装して独自のコントローラーファクトリを作成できますが、原則として、ニーズを満たすには、DefaultControllerFactoryクラスのメソッドをオーバーライドするだけで十分です。 通常、これはIController CreateController(RequestContext requestContext、string controllerName)です。 以下は、オーバーライドされたコントローラーファクトリの例です。



  public class MyControllerFactory : DefaultControllerFactory { public override IController CreateController(RequestContext requestContext, string controllerName) { //TODO: something Type controllerType = this.GetControllerType(requestContext, controllerName); return this.GetControllerInstance(requestContext, controllerType); } public override void ReleaseController(IController controller) { //TODO: something var disposable = controller as IDisposable; if (disposable != null) { disposable.Dispose(); } } protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType) { //TODO: something try { return (IController)Activator.CreateInstance(controllerType); } catch (Exception exception) { throw new InvalidOperationException(); } } }
      
      







ところで、Autofac、Ninject、Unityはすべて、Dependency Injection用の独自のコントローラーファクトリを作成します。

独自のファクトリの登録は、GlobalクラスのApplication_Start()メソッドでも行われます

ControllerBuilder.Current.SetControllerFactory(new MyControllerFactory());







3.アクション

コントローラインスタンスを作成した後、IController.Execute()メソッドが呼び出されます。 Execute()メソッドでは、必要なアクションを実行する必要があります。 標準のControllerクラスでは、制御はActionInvokerに転送され、特定のアクションを呼び出す役割を果たします。



 public class CustomActionInvoker : IActionInvoker { public bool InvokeAction(ControllerContext context, string actionName) { //TODO: invoke Action // if action was found // return true return false; } }
      
      







コンストラクターで初期化することで、コントローラーに登録できます。

 public class CustomActionInvokerController : Controller { public CustomActionInvokerController() { this.ActionInvoker = new CustomActionInvoker(); } }
      
      







ActionInvokerは、アクションを呼び出す前に ActionMethodSelectorAttributeおよびFilterAttribute (AuthorizeAttribute、HandleErrorAttibute、ValidateAntiForgeryTokenAttribute、ValidateInputAttribute)から派生したその属性もチェックします。 これらの属性により、制御を特定のアクションに移すことができるかどうかを決定できます。

独自のアクションセレクタを作成するには、この属性から継承します



 [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)] public abstract class ActionMethodSelectorAttribute : Attribute { public abstract bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo); }
      
      







4.バインダー

既に知っているように、MVCメカニズムでは、最も単純なパラメーター(int、string)に加えて、クラスなどのより複雑なパラメーターを使用できます。 ただし、標準のバインダーでは処理できない、かなりエキゾチックなクラスを受け入れる必要がある場合があります。 この場合、独自のバインダーを作成できます。 これを行うには、DefaultModelBinderを継承し、そのメソッドをオーバーライドするだけで十分です。



 public class CustomModelBinder : DefaultModelBinder { public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { //TODO: something return base.BindModel(controllerContext, bindingContext); } protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType) { //TODO: something return base.CreateModel(controllerContext, bindingContext, modelType); } protected override System.ComponentModel.PropertyDescriptorCollection GetModelProperties(ControllerContext controllerContext, ModelBindingContext bindingContext) { //TODO: something return base.GetModelProperties(controllerContext, bindingContext); } protected override void BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor) { //TODO: something base.BindProperty(controllerContext, bindingContext, propertyDescriptor); } protected override object GetPropertyValue(ControllerContext controllerContext, ModelBindingContext bindingContext, System.ComponentModel.PropertyDescriptor propertyDescriptor, IModelBinder propertyBinder) { return base.GetPropertyValue(controllerContext, bindingContext, propertyDescriptor, propertyBinder); } protected override ICustomTypeDescriptor GetTypeDescriptor(ControllerContext controllerContext, ModelBindingContext bindingContext) { //TODO: something return base.GetTypeDescriptor(controllerContext, bindingContext); } protected override void OnModelUpdated(ControllerContext controllerContext, ModelBindingContext bindingContext) { base.OnModelUpdated(controllerContext, bindingContext); } protected override bool OnModelUpdating(ControllerContext controllerContext, ModelBindingContext bindingContext) { //TODO: something return base.OnModelUpdating(controllerContext, bindingContext); } protected override void OnPropertyValidated(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor, object value) { base.OnPropertyValidated(controllerContext, bindingContext, propertyDescriptor, value); } protected override bool OnPropertyValidating(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor, object value) { //TODO: something return base.OnPropertyValidating(controllerContext, bindingContext, propertyDescriptor, value); } protected override void SetProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor, object value) { //TODO: something base.SetProperty(controllerContext, bindingContext, propertyDescriptor, value); } }
      
      







ご覧のとおり、基本クラスは多くの機会を提供してくれます。

ModelBinderをモデルに直接接続できます。 属性を使用します。



 [ModelBinder(typeof(CustomModelBinder))]
      
      







または、GlobalクラスのApplication_Start()メソッド。



 ModelBinders.Binders.Add(typeof(CustomTypeModel), new CustomModelBinder());
      
      







5 ActionResult

そして最後に、アクションが機能した後、onはActionResult()を返します。 View()、PartialView()、JsonResult()、FileResult()、EmptyResult()のいずれかです。

当然、このリストはActionResult()の実装により拡張および補完できます。 これを行うには、ActionResultクラスから継承し、void ExecuteResultメソッド(ControllerContextコンテキスト)をオーバーライドするだけで十分です。



 public class CustomResult : ActionResult { public override void ExecuteResult(ControllerContext context) { //TODO: something } }
      
      







必要なメソッドをView()に追加し、* .cshtmlファイルから直接使用する興味深い方法もあります。 これを行うには、WebViewPageクラスから継承します。



 public abstract class CustomView : WebViewPage { public string HelloWordPrint() { return "Hello Word"; } }
      
      







RazorView自体では、次のディレクティブを使用します



 @inherits CustomView
      
      







これで、ビューで新しいメソッドを使用できます。



おわりに

以上です。 この投稿では、MVCのリクエストのすべての段階を経て、各段階にどのように影響を与えることができるかを調べました。 MVC3フレームワークがどのように機能するかを完全に把握していただければ幸いです。



All Articles