ASP.NET MVCがどのように動作するか(3):Controllerのアクティブ化
21391 ワード
ASP.NET MVCのURLルーティングシステムは、登録されたルーティングテーブルを介してHTTP要求を解析してルーティングデータをカプセル化するためのRouteDataオブジェクトを得るが、このプロセスは、カスタムUrlRoutingModuleを介してHttpApplicationのPostResolveRequestCacheイベントを登録することによって実装される.RouteDataにはすでにターゲットコントローラの名前が含まれています.ここでは、真のコントローラオブジェクトがどのようにアクティブになっているかをさらに分析します.まず、MvcRouteHandlerのタイプを理解する必要があります.
一、MvcRouteHandler
一、MvcRouteHandler
先に説明したように、RouteBaseから継承されたRouteタイプには、指定されたリクエストコンテキスト(RequestContextオブジェクトで表される)に基づいてHttpHandlerオブジェクトを取得するためのIrouteHandlerインタフェースのタイプである属性RouteHandlerがあることがわかります.GetRouteDataメソッドが実行されると、RouteのRouteHandler属性値は、得られたRouteDataの同名属性に反映されます.デフォルトでは、RouteのRouteHandlerプロパティはMvcRouteHandlerオブジェクトであり、次のようなコード断片がこれを反映しています. 1: public class Route : RouteBase
2: {
3: //
4: public IRouteHandler RouteHandler { get; set; }
5: public Route()
6: {
7: //
8: this.RouteHandler = new MvcRouteHandler();
9: }
10: }
この「ミニ版」のASPについてNET MVCフレームワークでは、MvcRouteHandlerは、以下のように定義されたタイプである.実装されたGetHttpHandlerメソッドでは、MvcHandlerオブジェクトが直接返されます. 1: public class MvcRouteHandler: IRouteHandler
2: {
3: public IHttpHandler GetHttpHandler(RequestContext requestContext)
4: {
5: return new MvcHandler(requestContext);
6: }
7: }
二、MvcHandler
前述の内容ではASP全体について述べた.NET MVCフレームワークは、カスタムHttpModuleとHttpHandlerオブジェクトASP.NETは拡張を実現しています.このカスタムHttpModuleはすでに紹介しています.UrlRoutingModuleですが、このカスタムHttpHandlerは私たちが重点的に紹介するMvcHandlerです.
UrlRoutingModuleは、ルーティングテーブルによってHTTP要求を解析してルーティングデータをカプセル化するためのRouteDataを得た後、またはそのRouteHandlerのGetHttpHandlerメソッドを呼び出してHttpHandlerオブジェクトを得、現在のHTTPコンテキストに登録する.RouteDataのRouteHandlerはRouteオブジェクトに対応するRouteHandlerに由来し、後者はデフォルトではMvcRouteHandlerオブジェクトであるため、デフォルトでHTTPリクエストの処理に使用されるのはこのようなMvcHandlerオブジェクトである.MvcHandlerは、Controllerオブジェクトのアクティブ化と対応するActionメソッドの実行を実現します.
次のコード・スライスは、構造関数で指定されたRequestContextのタイプの属性を持つMvcHandlerの定義全体を表しています.実装されたProcessRequestでは、Controllerオブジェクトのアクティブ化と実行が実現されます. 1: public class MvcHandler: IHttpHandler
2: {
3: public bool IsReusable
4: {
5: get{return false;}
6: }
7: public RequestContext RequestContext { get; private set; }
8: public MvcHandler(RequestContext requestContext)
9: {
10: this.RequestContext = requestContext;
11: }
12: public void ProcessRequest(HttpContext context)
13: {
14: string controllerName = this.RequestContext.RouteData.Controller;
15: IControllerFactory controllerFactory = ControllerBuilder.Current.GetControllerFactory();
16: IController controller = controllerFactory.CreateController(this.RequestContext, controllerName);
17: controller.Execute(this.RequestContext);
18: }
19: }
三、ControllerとContrllerFactory
次のコード・スライスに示すように、MvcHandlerのProcessRequestメソッドで実行される一意のメソッドExecuteを有するインタフェースIControllerを定義し、メソッドのパラメータが入力されると現在のリクエストコンテキストを表すRequestContextオブジェクトを表す. 1: public interface IController
2: {
3: void Execute(RequestContext requestContext);
4: }
MvcHandlerの定義から、Controllerオブジェクトのアクティブ化はファクトリモードによって実現されることがわかります.Controllerファクトリには、次のように定義されたIControllerFactoryインタフェースが定義されています.IControllerFactoryは、CreateControllerメソッドを使用して、入力されたリクエストコンテキストとControllerの名前に基づいて、対応するControllerオブジェクトをアクティブにします. 1: public interface IControllerFactory
2: {
3: IController CreateController(RequestContext requestContext, string controllerName);
4: }
MvcHandlerのProcessRequestメソッドでは、現在のControllerBuilderオブジェクトをControllerBuilderの静的属性Currentで取得し、GetControllerFactoryメソッドを呼び出して現在のControllerFactoryを取得します.その後、自分のRequestContextから抽出したRouteDataからControllerの名前を取得し、最後にRequestContextとともにContollerFactoryのCreateControllerメソッドのパラメータとして具体的なControllerオブジェクトを作成します.
ControllerBuilderの定義全体は、現在のControllerBuilderの静的読取り専用属性を表すCurrentが静的コンストラクション関数で作成されるコード断片のように示されています.ContorllerFactoryメソッドとGetControllerFactoryメソッドは、ContorllerFactoryの登録および取得に使用されます.一方、タイプがHashSetのDefaultNamespacesプロパティは、Controllerタイプを最終的に解析するためのデフォルトのネーミングスペースリストを表します. 1: public class ControllerBuilder
2: {
3: private Func<IControllerFactory> factoryThunk;
4: static ControllerBuilder()
5: {
6: Current = new ControllerBuilder();
7: }
8: public ControllerBuilder()
9: {
10: this.DefaultNamespaces = new HashSet<string>();
11: }
12: public static ControllerBuilder Current { get; private set; }
13: public IControllerFactory GetControllerFactory()
14: {
15: return factoryThunk();
16: }
17: public void SetControllerFactory(IControllerFactory controllerFactory)
18: {
19: factoryThunk = () => controllerFactory;
20: }
21: public HashSet<string> DefaultNamespaces { get; private set; }
22: }
振り返る前にASPをカスタマイズしましたNET MVCフレームワークのWebアプリケーションは、現在のControllerBuilderでControllerFactoryの登録とデフォルトのネーミングスペースの指定を行います.次のコード・スライスに示すように、私たちが登録したControllerFactoryのタイプはDefaultControllerFactoryです. 1: public class Global : System.Web.HttpApplication
2: {
3: protected void Application_Start(object sender, EventArgs e)
4: {
5: //
6: ControllerBuilder.Current.SetControllerFactory(new DefaultControllerFactory());
7: ControllerBuilder.Current.DefaultNamespaces.Add("WebApp");
8: }
9: }
デフォルトのControllerFactoryであるDefaultControllerFactoryタイプは以下のように定義されています.Controllerタイプをアクティブにするには、Controllerの真のタイプを正しく解析できることが前提です.CreateControllerメソッドとしてパラメータを入力するcontrollerrNameは、Controllerの名前のみを表します.タイプ名としてController文字接尾辞を付ける必要があります.さらに、RouteDataと現在のControllerBuilderの2つのソースを持つタイプのネーミングスペースも必要です.DefoualtControllerFactoryの初期化では、BuildManagerを使用してすべてのアプリケーションのプログラムセットをロードし、インタフェースIControllerを実装したすべてのタイプをロードして保存します.CreateControllerメソッドでは、保存したControllerタイプリストからControllerの名前と名前空間に基づいて対応するControllerタイプを取得し、反射的に作成します. 1: public class DefaultControllerFactory : IControllerFactory
2: {
3: private List<Type> controllerTypes = new List<Type>();
4: public DefaultControllerFactory()
5: {
6: foreach (Assembly assembly in BuildManager.GetReferencedAssemblies())
7: {
8: foreach (Type type in assembly.GetTypes().Where(type => typeof(IController).IsAssignableFrom(type)))
9: {
10: controllerTypes.Add(type);
11: }
12: }
13: }
14: public IController CreateController(RequestContext requestContext, string controllerName)
15: {
16: string typeName = controllerName + "Controller";
17: List<string> namespaces = new List<string>();
18: namespaces.AddRange(requestContext.RouteData.Namespaces);
19: namespaces.AddRange(ControllerBuilder.Current.DefaultNamespaces);
20: foreach (var ns in namespaces)
21: {
22: string controllerTypeName = string.Format("{0}.{1}", ns, typeName);
23: Type controllerType = controllerTypes.FirstOrDefault(type => string.Compare(type.FullName, controllerTypeName, true) == 0);
24: if (null != controllerType)
25: {
26: return (IController)Activator.CreateInstance(controllerType);
27: }
28: }
29: return null;
30: }
31: }
上記では、Controllerのアクティブ化原理について詳しく説明しましたが、注目点はController自身に戻ります.IContrllerインタフェースを実装することによって,以下のように定義されたController Base抽象ベースクラスを持つControllerのために定義した.実装されたExecuteメソッドでは、ControllerBaseがインタフェースIActionInvokerを実装したオブジェクトを介してActionメソッドの実行を完了していることがわかります. 1: public abstract class ControllerBase: IController
2: {
3: protected IActionInvoker ActionInvoker { get; set; }
4: public ControllerBase()
5: {
6: this.ActionInvoker = new ControllerActionInvoker();
7: }
8: public void Execute(RequestContext requestContext)
9: {
10: ControllerContext context = new ControllerContext { RequestContext = requestContext, Controller = this };
11: string actionName = requestContext.RouteData.ActionName;
12: this.ActionInvoker.InvokeAction(context, actionName);
13: }
14: }
ASP.NET MVCがどのように動作するか[1]:「偽」MVCフレームワーク上に構築されたWebアプリケーション ASP.NET MVCがどのように動作するか[2]:URLルーティング ASP.NET MVCがどのように動作しているか[3]:Controllerがアクティブ化 ASP.NET MVCがどのように動作するか[4]:Actionの実行
1: public class Route : RouteBase
2: {
3: //
4: public IRouteHandler RouteHandler { get; set; }
5: public Route()
6: {
7: //
8: this.RouteHandler = new MvcRouteHandler();
9: }
10: }
1: public class MvcRouteHandler: IRouteHandler
2: {
3: public IHttpHandler GetHttpHandler(RequestContext requestContext)
4: {
5: return new MvcHandler(requestContext);
6: }
7: }
前述の内容ではASP全体について述べた.NET MVCフレームワークは、カスタムHttpModuleとHttpHandlerオブジェクトASP.NETは拡張を実現しています.このカスタムHttpModuleはすでに紹介しています.UrlRoutingModuleですが、このカスタムHttpHandlerは私たちが重点的に紹介するMvcHandlerです.
UrlRoutingModuleは、ルーティングテーブルによってHTTP要求を解析してルーティングデータをカプセル化するためのRouteDataを得た後、またはそのRouteHandlerのGetHttpHandlerメソッドを呼び出してHttpHandlerオブジェクトを得、現在のHTTPコンテキストに登録する.RouteDataのRouteHandlerはRouteオブジェクトに対応するRouteHandlerに由来し、後者はデフォルトではMvcRouteHandlerオブジェクトであるため、デフォルトでHTTPリクエストの処理に使用されるのはこのようなMvcHandlerオブジェクトである.MvcHandlerは、Controllerオブジェクトのアクティブ化と対応するActionメソッドの実行を実現します.
次のコード・スライスは、構造関数で指定されたRequestContextのタイプの属性を持つMvcHandlerの定義全体を表しています.実装されたProcessRequestでは、Controllerオブジェクトのアクティブ化と実行が実現されます.
1: public class MvcHandler: IHttpHandler
2: {
3: public bool IsReusable
4: {
5: get{return false;}
6: }
7: public RequestContext RequestContext { get; private set; }
8: public MvcHandler(RequestContext requestContext)
9: {
10: this.RequestContext = requestContext;
11: }
12: public void ProcessRequest(HttpContext context)
13: {
14: string controllerName = this.RequestContext.RouteData.Controller;
15: IControllerFactory controllerFactory = ControllerBuilder.Current.GetControllerFactory();
16: IController controller = controllerFactory.CreateController(this.RequestContext, controllerName);
17: controller.Execute(this.RequestContext);
18: }
19: }
三、ControllerとContrllerFactory
次のコード・スライスに示すように、MvcHandlerのProcessRequestメソッドで実行される一意のメソッドExecuteを有するインタフェースIControllerを定義し、メソッドのパラメータが入力されると現在のリクエストコンテキストを表すRequestContextオブジェクトを表す. 1: public interface IController
2: {
3: void Execute(RequestContext requestContext);
4: }
MvcHandlerの定義から、Controllerオブジェクトのアクティブ化はファクトリモードによって実現されることがわかります.Controllerファクトリには、次のように定義されたIControllerFactoryインタフェースが定義されています.IControllerFactoryは、CreateControllerメソッドを使用して、入力されたリクエストコンテキストとControllerの名前に基づいて、対応するControllerオブジェクトをアクティブにします. 1: public interface IControllerFactory
2: {
3: IController CreateController(RequestContext requestContext, string controllerName);
4: }
MvcHandlerのProcessRequestメソッドでは、現在のControllerBuilderオブジェクトをControllerBuilderの静的属性Currentで取得し、GetControllerFactoryメソッドを呼び出して現在のControllerFactoryを取得します.その後、自分のRequestContextから抽出したRouteDataからControllerの名前を取得し、最後にRequestContextとともにContollerFactoryのCreateControllerメソッドのパラメータとして具体的なControllerオブジェクトを作成します.
ControllerBuilderの定義全体は、現在のControllerBuilderの静的読取り専用属性を表すCurrentが静的コンストラクション関数で作成されるコード断片のように示されています.ContorllerFactoryメソッドとGetControllerFactoryメソッドは、ContorllerFactoryの登録および取得に使用されます.一方、タイプがHashSetのDefaultNamespacesプロパティは、Controllerタイプを最終的に解析するためのデフォルトのネーミングスペースリストを表します. 1: public class ControllerBuilder
2: {
3: private Func<IControllerFactory> factoryThunk;
4: static ControllerBuilder()
5: {
6: Current = new ControllerBuilder();
7: }
8: public ControllerBuilder()
9: {
10: this.DefaultNamespaces = new HashSet<string>();
11: }
12: public static ControllerBuilder Current { get; private set; }
13: public IControllerFactory GetControllerFactory()
14: {
15: return factoryThunk();
16: }
17: public void SetControllerFactory(IControllerFactory controllerFactory)
18: {
19: factoryThunk = () => controllerFactory;
20: }
21: public HashSet<string> DefaultNamespaces { get; private set; }
22: }
振り返る前にASPをカスタマイズしましたNET MVCフレームワークのWebアプリケーションは、現在のControllerBuilderでControllerFactoryの登録とデフォルトのネーミングスペースの指定を行います.次のコード・スライスに示すように、私たちが登録したControllerFactoryのタイプはDefaultControllerFactoryです. 1: public class Global : System.Web.HttpApplication
2: {
3: protected void Application_Start(object sender, EventArgs e)
4: {
5: //
6: ControllerBuilder.Current.SetControllerFactory(new DefaultControllerFactory());
7: ControllerBuilder.Current.DefaultNamespaces.Add("WebApp");
8: }
9: }
デフォルトのControllerFactoryであるDefaultControllerFactoryタイプは以下のように定義されています.Controllerタイプをアクティブにするには、Controllerの真のタイプを正しく解析できることが前提です.CreateControllerメソッドとしてパラメータを入力するcontrollerrNameは、Controllerの名前のみを表します.タイプ名としてController文字接尾辞を付ける必要があります.さらに、RouteDataと現在のControllerBuilderの2つのソースを持つタイプのネーミングスペースも必要です.DefoualtControllerFactoryの初期化では、BuildManagerを使用してすべてのアプリケーションのプログラムセットをロードし、インタフェースIControllerを実装したすべてのタイプをロードして保存します.CreateControllerメソッドでは、保存したControllerタイプリストからControllerの名前と名前空間に基づいて対応するControllerタイプを取得し、反射的に作成します. 1: public class DefaultControllerFactory : IControllerFactory
2: {
3: private List<Type> controllerTypes = new List<Type>();
4: public DefaultControllerFactory()
5: {
6: foreach (Assembly assembly in BuildManager.GetReferencedAssemblies())
7: {
8: foreach (Type type in assembly.GetTypes().Where(type => typeof(IController).IsAssignableFrom(type)))
9: {
10: controllerTypes.Add(type);
11: }
12: }
13: }
14: public IController CreateController(RequestContext requestContext, string controllerName)
15: {
16: string typeName = controllerName + "Controller";
17: List<string> namespaces = new List<string>();
18: namespaces.AddRange(requestContext.RouteData.Namespaces);
19: namespaces.AddRange(ControllerBuilder.Current.DefaultNamespaces);
20: foreach (var ns in namespaces)
21: {
22: string controllerTypeName = string.Format("{0}.{1}", ns, typeName);
23: Type controllerType = controllerTypes.FirstOrDefault(type => string.Compare(type.FullName, controllerTypeName, true) == 0);
24: if (null != controllerType)
25: {
26: return (IController)Activator.CreateInstance(controllerType);
27: }
28: }
29: return null;
30: }
31: }
上記では、Controllerのアクティブ化原理について詳しく説明しましたが、注目点はController自身に戻ります.IContrllerインタフェースを実装することによって,以下のように定義されたController Base抽象ベースクラスを持つControllerのために定義した.実装されたExecuteメソッドでは、ControllerBaseがインタフェースIActionInvokerを実装したオブジェクトを介してActionメソッドの実行を完了していることがわかります. 1: public abstract class ControllerBase: IController
2: {
3: protected IActionInvoker ActionInvoker { get; set; }
4: public ControllerBase()
5: {
6: this.ActionInvoker = new ControllerActionInvoker();
7: }
8: public void Execute(RequestContext requestContext)
9: {
10: ControllerContext context = new ControllerContext { RequestContext = requestContext, Controller = this };
11: string actionName = requestContext.RouteData.ActionName;
12: this.ActionInvoker.InvokeAction(context, actionName);
13: }
14: }
ASP.NET MVCがどのように動作するか[1]:「偽」MVCフレームワーク上に構築されたWebアプリケーション ASP.NET MVCがどのように動作するか[2]:URLルーティング ASP.NET MVCがどのように動作しているか[3]:Controllerがアクティブ化 ASP.NET MVCがどのように動作するか[4]:Actionの実行
1: public interface IController
2: {
3: void Execute(RequestContext requestContext);
4: }
1: public interface IControllerFactory
2: {
3: IController CreateController(RequestContext requestContext, string controllerName);
4: }
1: public class ControllerBuilder
2: {
3: private Func<IControllerFactory> factoryThunk;
4: static ControllerBuilder()
5: {
6: Current = new ControllerBuilder();
7: }
8: public ControllerBuilder()
9: {
10: this.DefaultNamespaces = new HashSet<string>();
11: }
12: public static ControllerBuilder Current { get; private set; }
13: public IControllerFactory GetControllerFactory()
14: {
15: return factoryThunk();
16: }
17: public void SetControllerFactory(IControllerFactory controllerFactory)
18: {
19: factoryThunk = () => controllerFactory;
20: }
21: public HashSet<string> DefaultNamespaces { get; private set; }
22: }
振り返る前にASPをカスタマイズしましたNET MVCフレームワークのWebアプリケーションは、現在のControllerBuilderでControllerFactoryの登録とデフォルトのネーミングスペースの指定を行います.次のコード・スライスに示すように、私たちが登録したControllerFactoryのタイプはDefaultControllerFactoryです.
1: public class Global : System.Web.HttpApplication
2: {
3: protected void Application_Start(object sender, EventArgs e)
4: {
5: //
6: ControllerBuilder.Current.SetControllerFactory(new DefaultControllerFactory());
7: ControllerBuilder.Current.DefaultNamespaces.Add("WebApp");
8: }
9: }
デフォルトのControllerFactoryであるDefaultControllerFactoryタイプは以下のように定義されています.Controllerタイプをアクティブにするには、Controllerの真のタイプを正しく解析できることが前提です.CreateControllerメソッドとしてパラメータを入力するcontrollerrNameは、Controllerの名前のみを表します.タイプ名としてController文字接尾辞を付ける必要があります.さらに、RouteDataと現在のControllerBuilderの2つのソースを持つタイプのネーミングスペースも必要です.DefoualtControllerFactoryの初期化では、BuildManagerを使用してすべてのアプリケーションのプログラムセットをロードし、インタフェースIControllerを実装したすべてのタイプをロードして保存します.CreateControllerメソッドでは、保存したControllerタイプリストからControllerの名前と名前空間に基づいて対応するControllerタイプを取得し、反射的に作成します.
1: public class DefaultControllerFactory : IControllerFactory
2: {
3: private List<Type> controllerTypes = new List<Type>();
4: public DefaultControllerFactory()
5: {
6: foreach (Assembly assembly in BuildManager.GetReferencedAssemblies())
7: {
8: foreach (Type type in assembly.GetTypes().Where(type => typeof(IController).IsAssignableFrom(type)))
9: {
10: controllerTypes.Add(type);
11: }
12: }
13: }
14: public IController CreateController(RequestContext requestContext, string controllerName)
15: {
16: string typeName = controllerName + "Controller";
17: List<string> namespaces = new List<string>();
18: namespaces.AddRange(requestContext.RouteData.Namespaces);
19: namespaces.AddRange(ControllerBuilder.Current.DefaultNamespaces);
20: foreach (var ns in namespaces)
21: {
22: string controllerTypeName = string.Format("{0}.{1}", ns, typeName);
23: Type controllerType = controllerTypes.FirstOrDefault(type => string.Compare(type.FullName, controllerTypeName, true) == 0);
24: if (null != controllerType)
25: {
26: return (IController)Activator.CreateInstance(controllerType);
27: }
28: }
29: return null;
30: }
31: }
上記では、Controllerのアクティブ化原理について詳しく説明しましたが、注目点はController自身に戻ります.IContrllerインタフェースを実装することによって,以下のように定義されたController Base抽象ベースクラスを持つControllerのために定義した.実装されたExecuteメソッドでは、ControllerBaseがインタフェースIActionInvokerを実装したオブジェクトを介してActionメソッドの実行を完了していることがわかります.
1: public abstract class ControllerBase: IController
2: {
3: protected IActionInvoker ActionInvoker { get; set; }
4: public ControllerBase()
5: {
6: this.ActionInvoker = new ControllerActionInvoker();
7: }
8: public void Execute(RequestContext requestContext)
9: {
10: ControllerContext context = new ControllerContext { RequestContext = requestContext, Controller = this };
11: string actionName = requestContext.RouteData.ActionName;
12: this.ActionInvoker.InvokeAction(context, actionName);
13: }
14: }
ASP.NET MVCがどのように動作するか[1]:「偽」MVCフレームワーク上に構築されたWebアプリケーション ASP.NET MVCがどのように動作するか[2]:URLルーティング ASP.NET MVCがどのように動作しているか[3]:Controllerがアクティブ化 ASP.NET MVCがどのように動作するか[4]:Actionの実行