ASP.NET MVC是如何运行的(3): Controller的激活
2012-03-12 09:19
1061 查看
ASP.NET MVC的URL路由系统通过注册的路由表对HTTP请求进行解析从而得到一个用于封装路由数据的RouteData对象,而这个过程是通过自定义的UrlRoutingModule对HttpApplication的PostResolveRequestCache事件进行注册实现的。RouteData中已经包含了目标Controller的名称,现在我们来进一步分析真正的Controller对象是如何被激活的。我们首先需要了解一个类型为MvcRouteHandler的类型。
[/code]
对于我们这个“迷你版”的ASP.NET MVC框架来说,MvcRouteHandler是一个具有如下定义的类型。在实现的GetHttpHandler方法中,它直接返回一个MvcHandler对象。
[/code]
UrlRoutingModule在通过路由表解析HTTP请求得到一个用于封装路由数据的RouteData后,或调用其RouteHandler的GetHttpHandler方法得到HttpHandler对象并注册到当前的HTTP上下文。由于RouteData的RouteHandler来源于对应Route对象的RouteHandler,而后者在默认的情况下是一个MvcRouteHandler对象,所以默认情况下用于处理HTTP请求的就是这么一个MvcHandler对象。MvcHandler实现了对Controller对象的激活和对相应Action方法的执行。
下面的的代码片断体现了MvcHandler的整个定义,它具有一个类型为RequestContext的属性表示被处理的当前请求上下文,该属性在构造函数指定。在实现的ProcessRequest中实现了对Controller对象的激活和执行。
[/code]
[/code]
从MvcHandler的定义我们可以看到Controller对象的激活是通过工厂模式实现的,我们为Controller工厂定义了一个具有如下定义的IControllerFactory接口。IControllerFactory通过CreateController方法根据传入的请求上下文和Controller的名称来激活相应的Controller对象。
[/code]
在MvcHandler的ProcessRequest方法中,它通过ControllerBuilder的静态属性Current得到当前的ControllerBuilder对象,并调用GetControllerFactory方法获得当前的ControllerFactory。然后通过从自己的RequestContext中提取的RouteData获得Controller的名称,最后将它连同RequestContext一起作为ContollerFactory的CreateController方法的参数进而创建具体的Controller对象。
ControllerBuilder的整个定义如下面的代码片断所示,表示当前ControllerBuilder的静态只读属性的Current在静态构造函数中被创建。SetControllerFactory和GetControllerFactory方法用于ContorllerFactory的注册和获取。而类型为HashSet<string>的DefaultNamespaces属性表示默认的命名空间列表,这是为了最终解析Controller类型的需要。
[/code]
在回头看看我们之前建立在我们自定义ASP.NET MVC框架的Web应用,我们就是通过当前的ControllerBuilder进行ControllerFactory的注册和默认命名空间的指定的。如下面的代码片断所示,我们注册的ControllerFactory的类型为DefaultControllerFactory。
[/code]
作为默认ControllerFactory的DefualtControllerFactory类型定义如下。激活Controller类型的前提是能够正确解析出Controller的真实类型。作为CreateController方法输入参数的controllerName仅仅表示Controller的名称,我们需要加上Controller字符后缀作为类型名称。此外我们还需要得到类型的命名空间,而命名空间具有两个来源,即RouteData和当前ControllerBuilder。在DefualtControllerFactory初始化过程中,我们通过BuildManager加载所有应用的程序集,并加载所有实现了接口IController的类型并保存起来,而在CreateController方法中根据Controller的名称和命名空间从保存的Controller类型列表中得到对应的Controller类型,并通过反射的方式创建它。
[/code]
上面我们详细地介绍了Controller的激活原理,我们现在讲关注点返回到Controller自身。通过实现IContrller接口,我们为具有的Controller定义了一个具有如下定义的ControllerBase抽象基类。从中我们可以看到在实现的Execute方法中,ControllerBase通过一个实现了接口IActionInvoker的对象完成了针对Action方法的执行。
[/code]
ASP.NET MVC是如何运行的[1]: 建立在“伪”MVC框架上的Web应用
ASP.NET MVC是如何运行的[2]: URL路由
ASP.NET MVC是如何运行的[3]: Controller击激活
ASP.NET MVC是如何运行的[4]: Action的执行
一、MvcRouteHandler
通过前面的介绍我们知道继承自RouteBase的Route类型具有一个类型为IRouteHandler接口的属性RouteHandler,它主要的用途就是用于根据指定的请求上下文(通过一个RequestContext对象表示)来获取一个HttpHandler对象。当GetRouteData方法被执行后,Route的RouteHandler属性值将反映在得到的RouteData的同名属性上。在默认的情况下,Route的RouteHandler属性是一个MvcRouteHandler对象,如下的代码片断反映了这一点。[code] public class Route : RouteBase { //其他成员 public IRouteHandler RouteHandler { get; set; } public Route() { //其他操作 this.RouteHandler = new MvcRouteHandler(); } }
[/code]
对于我们这个“迷你版”的ASP.NET MVC框架来说,MvcRouteHandler是一个具有如下定义的类型。在实现的GetHttpHandler方法中,它直接返回一个MvcHandler对象。
[code] public class MvcRouteHandler: IRouteHandler { public IHttpHandler GetHttpHandler(RequestContext requestContext) { return new MvcHandler(requestContext); } }
[/code]
二、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方法的执行。
下面的的代码片断体现了MvcHandler的整个定义,它具有一个类型为RequestContext的属性表示被处理的当前请求上下文,该属性在构造函数指定。在实现的ProcessRequest中实现了对Controller对象的激活和执行。
[code] public class MvcHandler: IHttpHandler { public bool IsReusable { get{return false;} } public RequestContext RequestContext { get; private set; } public MvcHandler(RequestContext requestContext) { this.RequestContext = requestContext; } public void ProcessRequest(HttpContext context) { string controllerName = this.RequestContext.RouteData.Controller; IControllerFactory controllerFactory = ControllerBuilder.Current.GetControllerFactory(); IController controller = controllerFactory.CreateController(this.RequestContext, controllerName); controller.Execute(this.RequestContext); } }
[/code]
三、Controller与ContrllerFactory
我们为Controller定义了一个接口IController,如下面的代码片断所示,该接口具有唯一的方法Execute在MvcHandler的ProcessRequest方法中被执行,而传入该方法的参数时表示当前请求上下文的RequestContext对象。[code] public interface IController { void Execute(RequestContext requestContext); }
[/code]
从MvcHandler的定义我们可以看到Controller对象的激活是通过工厂模式实现的,我们为Controller工厂定义了一个具有如下定义的IControllerFactory接口。IControllerFactory通过CreateController方法根据传入的请求上下文和Controller的名称来激活相应的Controller对象。
[code] public interface IControllerFactory { IController CreateController(RequestContext requestContext, string controllerName); }
[/code]
在MvcHandler的ProcessRequest方法中,它通过ControllerBuilder的静态属性Current得到当前的ControllerBuilder对象,并调用GetControllerFactory方法获得当前的ControllerFactory。然后通过从自己的RequestContext中提取的RouteData获得Controller的名称,最后将它连同RequestContext一起作为ContollerFactory的CreateController方法的参数进而创建具体的Controller对象。
ControllerBuilder的整个定义如下面的代码片断所示,表示当前ControllerBuilder的静态只读属性的Current在静态构造函数中被创建。SetControllerFactory和GetControllerFactory方法用于ContorllerFactory的注册和获取。而类型为HashSet<string>的DefaultNamespaces属性表示默认的命名空间列表,这是为了最终解析Controller类型的需要。
[code] public class ControllerBuilder { private Func<IControllerFactory> factoryThunk; static ControllerBuilder() { Current = new ControllerBuilder(); } public ControllerBuilder() { this.DefaultNamespaces = new HashSet<string>(); } public static ControllerBuilder Current { get; private set; } public IControllerFactory GetControllerFactory() { return factoryThunk(); } public void SetControllerFactory(IControllerFactory controllerFactory) { factoryThunk = () => controllerFactory; } public HashSet<string> DefaultNamespaces { get; private set; } }
[/code]
在回头看看我们之前建立在我们自定义ASP.NET MVC框架的Web应用,我们就是通过当前的ControllerBuilder进行ControllerFactory的注册和默认命名空间的指定的。如下面的代码片断所示,我们注册的ControllerFactory的类型为DefaultControllerFactory。
[code] public class Global : System.Web.HttpApplication { protected void Application_Start(object sender, EventArgs e) { //其他操作 ControllerBuilder.Current.SetControllerFactory(new DefaultControllerFactory()); ControllerBuilder.Current.DefaultNamespaces.Add("WebApp"); } }
[/code]
作为默认ControllerFactory的DefualtControllerFactory类型定义如下。激活Controller类型的前提是能够正确解析出Controller的真实类型。作为CreateController方法输入参数的controllerName仅仅表示Controller的名称,我们需要加上Controller字符后缀作为类型名称。此外我们还需要得到类型的命名空间,而命名空间具有两个来源,即RouteData和当前ControllerBuilder。在DefualtControllerFactory初始化过程中,我们通过BuildManager加载所有应用的程序集,并加载所有实现了接口IController的类型并保存起来,而在CreateController方法中根据Controller的名称和命名空间从保存的Controller类型列表中得到对应的Controller类型,并通过反射的方式创建它。
[code] public class DefaultControllerFactory : IControllerFactory { private List<Type> controllerTypes = new List<Type>(); public DefaultControllerFactory() { foreach (Assembly assembly in BuildManager.GetReferencedAssemblies()) { foreach (Type type in assembly.GetTypes().Where(type => typeof(IController).IsAssignableFrom(type))) { controllerTypes.Add(type); } } } public IController CreateController(RequestContext requestContext, string controllerName) { string typeName = controllerName + "Controller"; List<string> namespaces = new List<string>(); namespaces.AddRange(requestContext.RouteData.Namespaces); namespaces.AddRange(ControllerBuilder.Current.DefaultNamespaces); foreach (var ns in namespaces) { string controllerTypeName = string.Format("{0}.{1}", ns, typeName); Type controllerType = controllerTypes.FirstOrDefault(type => string.Compare(type.FullName, controllerTypeName, true) == 0); if (null != controllerType) { return (IController)Activator.CreateInstance(controllerType); } } return null; } }
[/code]
上面我们详细地介绍了Controller的激活原理,我们现在讲关注点返回到Controller自身。通过实现IContrller接口,我们为具有的Controller定义了一个具有如下定义的ControllerBase抽象基类。从中我们可以看到在实现的Execute方法中,ControllerBase通过一个实现了接口IActionInvoker的对象完成了针对Action方法的执行。
[code] public abstract class ControllerBase: IController { protected IActionInvoker ActionInvoker { get; set; } public ControllerBase() { this.ActionInvoker = new ControllerActionInvoker(); } public void Execute(RequestContext requestContext) { ControllerContext context = new ControllerContext { RequestContext = requestContext, Controller = this }; string actionName = requestContext.RouteData.ActionName; this.ActionInvoker.InvokeAction(context, actionName); } }
[/code]
ASP.NET MVC是如何运行的[1]: 建立在“伪”MVC框架上的Web应用
ASP.NET MVC是如何运行的[2]: URL路由
ASP.NET MVC是如何运行的[3]: Controller击激活
ASP.NET MVC是如何运行的[4]: Action的执行
相关文章推荐
- 004. Asp.Net Routing与MVC 之二: 请求如何激活Controller和Action
- Asp.net MVC的Controller激活理解【学习笔记】
- .NET/ASP.NET MVC Controller 控制器(一:深入解析控制器运行原理)
- 如何在ASP.NET MVC 中获取当前URL、controller、action
- 学习ASP.NET MVC5框架揭秘笔记-ASP.NET MVC是如何运行的(四)
- asp.net mvc源码分析-Controllerl篇 如何创建Controller实例
- 目标HttpController在ASP.NET Web API中是如何被激活的:目标HttpController的选择
- ASP.NET MVC Controller的激活
- 白话ASP.NET MVC之三:Controller是如何解析出来的
- ASP.NET MVC, 从 Http Request 到 Controller -- Controller 是如何被精确定位的《一》
- 通过一个模拟程序让你明白ASP.NET MVC是如何运行的
- .NET/ASP.NETMVC Controller 控制器(一:深入解析控制器运行原理)
- ASP.NET MVC Controller激活系统详解
- ASP.NET MVC Controller的激活
- ASP.NET MVC Controller激活系统详解:默认实现
- ASP.NET MVC Controller的激活
- 通过一个模拟程序让你明白ASP.NET MVC是如何运行的
- ASP.NET MVC:如何提供 Controller 继承体系使用的 ModelBinder?
- ASP.NET Web API的HttpController是如何被激活的?
- 如何在ASP.NET MVC 中获取当前URL、controller、action