ASP.NET MVC应用框架是通过扩展ASP.NET实现的。ASP.NET的扩展点体现在HttpModule和HttpHandler两个核心组件上,ASP.NET MVC框架就是通过自定义HttpModule(UrlRoutingModule)HttpHandler(MvcHandler)实现的。
自定义HttpModule UrlRouteModule==>RouteTable==>Route==>RouteData
自定义Module:UrlRouteModule
解析HTTP请求获取一个以Controller和Action名称为核心的路由数据RouteData。
注册自定义Module 1 2 3 4 5 6 7 <system.webServer > <modules > <add name ="UrlRoutingModule" type ="CHCMVC.Module.UrlRoutingModule" /> </modules > </system.webServer >
注册Route对象 1 2 3 4 5 6 7 //添加ASP.NET应用程序文件Global.asax //在Application_Start方法中注册Route protected void Application_Start(object sender, EventArgs e) { //添加路由对象到路由表中 RouteTable.Routes.Add("default", new Route.Route { Url = "{controller}/{action}" }); }
路由表RouteTable 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 //路由表:路由对象的集合,静态属性Routes,通过Global.asax文件已注册路由对象 namespace CHCMVC.Route { /// <summary> /// 同一个Web应用可以采用不同的URL模式,所以需要注册多个路由对象,就组成了一个路由表 /// </summary> public class RouteTable { /// <summary> /// 路由对象列表 /// </summary> public static RouteDictionary Routes { get; set; } static RouteTable() { Routes = new RouteDictionary(); } } } //路由表中的静态属性Routes,通过遍历路由列表, //调用每个路由对象Route的GetRouteData方法可以获取与当前请求匹配的路由数据RouteData。 namespace CHCMVC.Route { public class RouteDictionary : Dictionary<string, RouteBase> { /// <summary> /// 获取与请求匹配的路由数据对象 /// </summary> /// <param name="httpContext"></param> /// <returns></returns> public RouteData GetRouteData(HttpContextBase httpContext) { foreach (var route in this.Values) { RouteData routeData = route.GetRouteData(httpContext); if (null != routeData) { return routeData; } } return null; } } }
路由对象Route 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 //通过路由对象Route,可以获取该路由对象的路由数据RouteData。 //RouteData继承抽象类RouteBase,实现抽象方法GetRouteData。 namespace CHCMVC.Route { public abstract class RouteBase { public abstract RouteData GetRouteData(HttpContextBase httpContext); } } //将请求的URL与路由对象的URL(已在Global.asax文件中注册)匹配 //路由对象里面的RouteHandler来源于自定义的Handler,MvcRouteHandler==>MvcHandler会赋给RouteData。 namespace CHCMVC.Route { /// <summary> /// 路由数据RouteData里面的属性,来源于路由对象Route的同名属性 /// </summary> public class Route : RouteBase { public IRouteHandler RouteHandler { get; set; } /// <summary> /// 当前请求的URL /// </summary> public string Url { get; set; } public IDictionary<string, object> DataTokens { get; set; } public Route() { this.DataTokens = new Dictionary<string, object>(); this.RouteHandler = new MvcRouteHandler();//自定义Handler } /// <summary> /// 通过HttpContextBase获取请求地址,创建RouteData对象 /// </summary> /// <param name="httpContext"></param> /// <returns></returns> public override RouteData GetRouteData(HttpContextBase httpContext) { IDictionary<string, object> variables; if (this.Match(httpContext.Request.AppRelativeCurrentExecutionFilePath.Substring(2), out variables)) { RouteData routeData = new RouteData(); foreach (var item in variables) { routeData.Values.Add(item.Key, item.Value); } foreach (var item in DataTokens) { routeData.DataTokens.Add(item.Key, item.Value); } routeData.RouteHandler = this.RouteHandler; return routeData; } return null; } /// <summary> /// 将请求的URL与路由对象Route中的URL比较,获取请求URL的Controller和Action /// </summary> /// <param name="requestUrl"></param> /// <param name="variables"></param> /// <returns></returns> protected bool Match(string requestUrl, out IDictionary<string, object> variables) { variables = new Dictionary<string, object>(); string[] strArray1 = requestUrl.Split('/'); string[] strArray2 = this.Url.Split('/'); if (strArray1.Length != strArray2.Length) { return false; } for (int i = 0; i < strArray2.Length; i++) { if (strArray2[i].StartsWith("{") && strArray2[i].EndsWith("}")) { variables.Add(strArray2[i].Trim("{}".ToArray()), strArray1[i]); } } return true; } } }
路由数据RouteData 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 //通过自定义HttpModule【UrlRouteModule】获得路由数据RouteData。 //路由数据保存了对应的Controller和Action名称,通过反射实现激活。 namespace CHCMVC.Route { /// <summary> /// ASP.NET MVC中的路由数据:RouteData,以Controller和Action名称为核心 /// </summary> public class RouteData { /// <summary> /// Values:从请求地址中解析出的变量==》来源于Route:Values /// </summary> public IDictionary<string, object> Values { get; set; } /// <summary> /// DataTokes:其他变量==》来源于Route:DataTokens /// </summary> public IDictionary<string, object> DataTokens { get; set; } /// <summary> /// 处理Http请求==》来源于Route:RouteHandler /// </summary> public IRouteHandler RouteHandler { get; set; } public RouteData() { this.Values = new Dictionary<string, object>(); this.DataTokens = new Dictionary<string, object>(); this.DataTokens.Add("namespaces", new List<string>()); } //从Values中获取Controller名称 public string Controller { get { object controllerName = string.Empty; this.Values.TryGetValue("controller", out controllerName); return controllerName.ToString(); } } //从Values获取Action名称 public string Action { get { object actionName = string.Empty; this.Values.TryGetValue("action", out actionName); return actionName.ToString(); } } //从DataTokens中获取Namespaces名称 public IEnumerable<string> Namespaces { get { return (IEnumerable<string>)this.DataTokens["namespaces"]; } } } }
UrlRouteModule实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 //自定义的HttpModule,实现IHttpModule接口,在初始化方法Init中注册PostResolveRequestCache事件。 //在事件中调用Route对象的GetRouteData方法获取路由数据RouteData。 //将路由数据RouteData和Http请求上下文HttpContext封装一起,供Controller激活使用。 //通过RouteHandler调用GetHttpHandler获取自定义的HandlerHandler //调用RemapHandler触发HttpHandler处理请求,激活Controller namespace CHCMVC.Module { /// <summary> /// 自定义HttpModle:处理Http请求,解析出路由数据 /// </summary> public class UrlRoutingModule : IHttpModule { public void Dispose() { } public void Init(HttpApplication context) { context.PostResolveRequestCache += context_PostResolveRequestCache; } protected virtual void context_PostResolveRequestCache(object sender, EventArgs e) { HttpContextWrapper httpContext = new HttpContextWrapper(HttpContext.Current); //从路由表中获取路由数据 RouteData routeData = RouteTable.Routes.GetRouteData(httpContext); if (null == routeData) return; RequestContext requestContext = new RequestContext { RouteData = routeData, HttpContext = httpContext }; IHttpHandler handler = routeData.RouteHandler.GetHttpHandler(requestContext); httpContext.RemapHandler(handler); } } } //由于Controller激活需要用到路由数据RouteData,因此需要封装一起 namespace CHCMVC.Context { /// <summary> /// 当前请求上下文和路由数据RouteData的封装 /// </summary> public class RequestContext { public virtual HttpContextBase HttpContext { get; set; } public virtual RouteData RouteData { get; set; } } }
自定义HttpHandler MvcRouteHandler==>MvcHandler==>ControllerFactory==>Controller
ControllerActionInvoker==>ModelBinder==>ActionResult
自定义Handler:MvcRouteHandler
根据Controller名称通过反射激活Controller,根据Action名称通过反射执行Action。
注册Controller默认工厂 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 //添加ASP.NET应用程序文件Global.asax //在Application_Start方法中注册Route protected void Application_Start(object sender, EventArgs e) { //添加路由对象到路由表中 RouteTable.Routes.Add("default", new Route.Route { Url = "{controller}/{action}" }); //设置激活Controller的工厂 ControllerBuilder.Current.SetControllerFactory(new DefaultControllerFactory()); //设置激活Controller的命名空间 ControllerBuilder.Current.DefaultNamespaces.Add("CHCMVC.Controller"); } //默认工厂的注册通过ControllerBuilder实现 namespace CHCMVC.Controller { public class ControllerBuilder { private Func<IControllerFactory> factoryThunk; public HashSet<string> DefaultNamespaces { get; set; } public static ControllerBuilder Current { get; private set; } //单例 static ControllerBuilder() { Current = new ControllerBuilder(); } public ControllerBuilder() { this.DefaultNamespaces = new HashSet<string>(); } //获取默认工厂 public IControllerFactory GetControllerFactory() { return factoryThunk(); } //设置默认工厂 public void SetControllerFactory(IControllerFactory controllerFactory) { factoryThunk = () => controllerFactory; } } }
Controller工厂实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 //工厂接口 namespace CHCMVC.Controller { /// <summary> /// Controller对象的激活是通过工厂模式实现的 /// </summary> public interface IControllerFactory { IController CreateController(RequestContext requestContext, string controllerName); } } //工厂接口实现 //工厂的默认实现已经在Global.asax文件中注册,保存了所有实现IController接口的Controller类型 //工厂的CreateController用于Controller的创建 namespace CHCMVC.Controller { /// <summary> /// 激活Controller的默认工厂,通过反射创建Controller /// </summary> public class DefaultControllerFactory : IControllerFactory { private List<Type> controllerTypes = new List<Type>(); public DefaultControllerFactory() { //通过BuildManager加载所有的程序集,并保存了实现了IController接口的类型 foreach (Assembly assembly in BuildManager.GetReferencedAssemblies()) { foreach (Type type in assembly.GetTypes().Where(type => typeof(IController).IsAssignableFrom(type))) { controllerTypes.Add(type); } } } /// <summary> /// 根据Controller名字,通过反射创建Controller /// </summary> /// <param name="requestContext"></param> /// <param name="controllerName"></param> /// <returns></returns> 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; } } }
MvcRouteHandler激活Controller 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 //MvcRouteHandler已经在路由对象Route中实例化 //在自定义HttpModule中触发ProcessRequest方法,通过调用ControllerFactory创建Controller namespace CHCMVC.Handler { public class MvcRouteHandler : IRouteHandler { public IHttpHandler GetHttpHandler(RequestContext requestContext) { return new MvcHandler(requestContext); } } } //Controller创建(激活)在自定义HttpModule(UrlRouteModule)中触发 //调用Controller工厂实现 //MvcHandler调用ActionInvoker的Execute方法实现Action执行(激活) namespace CHCMVC.Handler { /// <summary> /// 自定义Handler:实现对Controller激活和Action执行 /// </summary> public class MvcHandler : IHttpHandler { public RequestContext RequestContext { get; set; } public MvcHandler(RequestContext requestContext) { this.RequestContext = requestContext; } public bool IsReusable { get { return false; } } 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); } } }
Action执行 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 //ControllerBase是请求Controller的基类 //实例化ControllerActionInvoker //实现对Action的执行 //由于Action的激活,离不开Controller,需要通过Controller反射方法,因此需要将Controller和RequestContext封装,供Action激活使用 namespace CHCMVC.Controller { public 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.Action; this.ActionInvoker.InvokeAction(context, actionName); } } } //Action的执行,可能Action拥有参数,因此需要参数绑定 //Action执行之后,返回ActionResult,在View中显示 namespace CHCMVC.Action { public class ControllerActionInvoker : IActionInvoker { /// <summary> /// 参数绑定 /// </summary> public IModelBinder ModelBinder { get; private set; } public ControllerActionInvoker() { this.ModelBinder = new DefaultModelBinder(); } public void InvokeAction(ControllerContext controllerContext, string actionName) { MethodInfo method = controllerContext.Controller.GetType().GetMethods().First(m => string.Compare(actionName, m.Name, true) == 0); List<object> parameters = new List<object>(); foreach (ParameterInfo parameter in method.GetParameters()) { parameters.Add(this.ModelBinder.BindModel(controllerContext, parameter.Name, parameter.ParameterType)); } ActionResult actionResult = method.Invoke(controllerContext.Controller, parameters.ToArray()) as ActionResult; actionResult.ExecuteResult(controllerContext); } } }
Model绑定 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 //Model绑定接口 namespace CHCMVC.Model { public interface IModelBinder { object BindModel(ControllerContext controllerContext,string modelName,Type modelType); } } //Model绑定接口实现 namespace CHCMVC.Model { /// <summary> /// 参数绑定: /// 来源:HTTP-POST Form、 RouteData、DataTokens /// 简单类型:匹配 /// 复杂类型:反射 /// </summary> public class DefaultModelBinder : IModelBinder { public object BindModel(ControllerContext controllerContext, string modelName, Type modelType) { if (modelType.IsValueType || typeof(string) == modelType) { object instance; if (GetValueTypeInstance(controllerContext, modelName, modelType, out instance)) { return instance; } return Activator.CreateInstance(modelType); } object modelInstance = Activator.CreateInstance(modelType); foreach (PropertyInfo property in modelType.GetProperties()) { if (!property.CanWrite || (!property.PropertyType.IsValueType && property.PropertyType != typeof(string))) { continue; } object propertyValue; if (GetValueTypeInstance(controllerContext, property.Name, property.PropertyType, out propertyValue)) { property.SetValue(modelInstance, propertyValue, null); } } return modelInstance; } private bool GetValueTypeInstance(ControllerContext controllerContex, string modelName, Type modelType, out object value) { var form = HttpContext.Current.Request.Form; string key; //HTTPPOST Form if (null != form) { key = form.AllKeys.FirstOrDefault(k => string.Compare(k, modelName, true) == 0); if (key != null) { value = Convert.ChangeType(form[key], modelType); return true; } } //RouteData.Values key = controllerContex.RequestContext.RouteData.Values.Where( item => string.Compare(item.Key, modelName, true) == 0).Select(item => item.Key).FirstOrDefault(); if (null != key) { value = Convert.ChangeType(controllerContex.RequestContext.RouteData.Values[key], modelType); return true; } //RouteData.DataTokens key = controllerContex.RequestContext.RouteData.DataTokens .Where(item => string.Compare(item.Key, modelName, true) == 0) .Select(item => item.Key).FirstOrDefault(); if (null != key) { value = Convert.ChangeType(controllerContex.RequestContext.RouteData.DataTokens[key], modelType); return true; } value = null; return false; } } }
View呈现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 //View呈现抽象类 namespace CHCMVC.View { /// <summary> /// 响应输出 /// </summary> public abstract class ActionResult { public abstract void ExecuteResult(ControllerContext context); } } //View呈现具体实现 namespace CHCMVC.View { public class RawContentResult : ActionResult { public string RowData { get; set; } public RawContentResult(string rowData) { this.RowData = rowData; } /// <summary> /// 将内容输出到页面上 /// </summary> /// <param name="context"></param> public override void ExecuteResult(ControllerContext context) { context.RequestContext.HttpContext.Response.Write(this.RowData); } } }
发起请求 请求Model 1 2 3 4 5 6 7 8 namespace CHCMVC.Model { public class SimpleModel { public string Controller { get; set; } public string Action { get; set; } } }
请求Controller 1 2 3 4 5 6 7 8 9 10 11 namespace CHCMVC.Controller { public class HomeController : ControllerBase { public ActionResult Index(SimpleModel model) { string content = string.Format("Controller: {0}<br/>Action:{1}", model.Controller, model.Action); return new RawContentResult(content); } } }
请求地址 http://localhost:9077/Home/Index
请求结果 Controller: Home
Action:Index