ASP.NET MVC-URL路由
MVC-即Model,View,Controller,三层架构模式。model作为领域模型,是维持应用状态,提供业务功能的领域模型;View-指UI层,用于和用户的交和页面的展示;而Controller则是定义具体了UI
逻辑功能。传统的MVC模式,并没有严格的定义,即Model层也可以直接返回给view层数据状态的变化,而非通过Controller来控制的。ASP.NET MVC中,参考了java中的Model2,使得view层和model层是隔离的,不可直接交互的。具体实现方式是,通过定义了一个拦截器-HttpModule,来处理http请求。分析请求中的Controller和Action,Controller被激活后,Action被执行。对于传入Action方法的参数,则是源于请求地址的get或post数据。
在Controller和Action被执行过程中,它可以调用Model获取或改变其状态。在Action方法执行的最后阶段,选择相应的View,绑定在view上的数据来源与model或基于显示要求进行的简单逻辑计算,我们有时称它为VM-View Model。生成的View最终写入Http回复并返回到浏览器中。
Model2中完全隔断了View和Model中的联系。Controller作为支配者在model2中尤为明显,用户请求不在由view报告给Controller,而是有拦截器直接转发给Controller。Controller不仅决定着Model的调用,还决定着View的选择和生成。其拦截机制是通过一个自定义的的HttpModule和一个自定义的HttpHandler来实现的。
一。ASP.NET MVC是如何运行的-URL路由
ASP.NET定义了一个全局的路由表(RouteTable),路由表中的每个路由对象(RouteData)对应着一个将Comtroller和Action名称作为占位符的URL模板。对于,每个抵达的HTTP请求,ASP.NET MVC会遍历路由表找到一个URL模板的模式与Http请求地址相匹配的路由对象,并最终解析出以Controller和Action名称为核心的路由对象。
路由对象UML类图:
具体代码如下:
1 namespace WebMvc 2 { 3 public class RouteData 4 { 5 /// <summary> 6 /// 包含解析http请求的变量。key可以为controller和action,存储请求中控制器名称和Action名称 7 /// </summary> 8 public IDictionary<string,object> Values { get; private set; } 9 10 /// <summary> 11 /// 表示其他类型变量。属性Namespaces的值从中获取 12 /// </summary> 13 public IDictionary<string,object> DataTokens { get; private set; } 14 15 public RouteData() 16 { 17 Values = new Dictionary<string, object>(); 18 DataTokens = new Dictionary<string, object>(); 19 DataTokens.Add("namespance", new List<string>()); 20 } 21 /// <summary> 22 /// 获取一个实际处理请求的IHttpHandler对象 23 /// </summary> 24 public IRouteHandler RouteHandler { get; set; } 25 26 /// <summary> 27 /// 返回一个匹配http请求路径的url模式的routeData对象 28 /// </summary> 29 public RouteBase Route { get; set; } 30 31 public string Controller 32 { 33 get 34 { 35 object controllername = string.Empty; 36 this.Values.TryGetValue("cotroller", out controllername); 37 return (string)controllername; 38 } 39 } 40 public string ActionName 41 { 42 get 43 { 44 object actionname = string.Empty; 45 this.Values.TryGetValue("action", out actionname); 46 return (string)actionname;//拆箱 47 } 48 } 49 50 public IEnumerable<string> Namespance 51 { 52 get 53 { 54 return (IEnumerable<string>)this.DataTokens["namespance"]; 55 } 56 } 57 } 58 }
1 namespace WebMvc 2 { 3 /// <summary> 4 /// 主要具有返回一个具体处理请求的IHttpHandler对象的GetHttpHandler方法 5 /// </summary> 6 public interface IRouteHandler 7 { 8 IHttpHandler GetHttpHandler(RequestContext requestContext); 9 } 10 }
1 namespace WebMvc 2 { 3 /// <summary> 4 /// Http请求上下文 5 /// 封装了一个HttpContext对象和RouteData对象 6 /// </summary> 7 public class RequestContext 8 { 9 public virtual HttpContext HttpContext { get; set; } 10 11 public virtual RouteData RouteData { get; set; } 12 13 public RequestContext(HttpContext httpContext, RouteData routeData) 14 { 15 this.HttpContext = httpContext; 16 this.RouteData = routeData; 17 } 18 } 19 }
二。Route和RouteTable
RouteData具有一个类型为RouteBase的Route属性,表示当前路由表中与当前请求相匹配的路由对象。也就是说,当前的RouteData就是通过这个路由对象对当前Http解析获得的。RouteBase是个抽象类,是所有路由对象的基类。主要包含一个返回当前路由对象的方法GetRouteData..
1 namespace WebMvc 2 { 3 /// <summary> 4 /// 通过对以HttpContextBase对象表示的Http上下文进行解析,从而获取到一个RouteData对象 5 /// </summary> 6 public abstract class RouteBase 7 { 8 public abstract RouteData GetRouteDara(HttpContextBase httpContextBase); 9 } 10 }
ASP.NET MVC 提供的基于URL模板的路由机制,是通过Route类实现的。Route是RouteBase的子类。UML类图:
1 namespace WebMvc 2 { 3 /// <summary> 4 /// MVC的基于URL模板的路由机制,是通过Route类实现的 5 /// </summary> 6 public class Route:RouteBase 7 { 8 /// <summary> 9 /// 处理http请求的对象 10 /// </summary> 11 public IRouteHandler RouteHandler { get; set; } 12 13 /// <summary> 14 /// 代表定义的URL模板 15 /// </summary> 16 public string Url { get; set; } 17 18 /// <summary> 19 /// 其他类型变量 20 /// </summary> 21 public IDictionary<string,object> DataToken { get; set; } 22 23 public Route() 24 { 25 this.DataToken = new Dictionary<string, object>(); 26 this.RouteHandler = new MvcRouteHandler(); 27 } 28 29 /// <summary> 30 /// 比较URL模板与http请求路径是否一致。 31 /// </summary> 32 /// <param name="requestUrl">http请求路径</param> 33 /// <param name="variables">返回一个存储字典,存储http请求路径的actio和controller名称</param> 34 /// <returns></returns> 35 protected bool Match(string requestUrl, out IDictionary<string, object> variables) 36 { 37 variables = new Dictionary<string, object>(); 38 var arr_1 = requestUrl.Split('/'); 39 var arr_2 = Url.Split('/'); 40 if (arr_1.Length != arr_2.Length) 41 return false; 42 for (var k = 0; k <= arr_1.Length - 1; k++) 43 { 44 if (arr_2[k].StartsWith("{") && arr_2[k].EndsWith("}")) 45 { 46 variables.Add(arr_2[k].Trim("{}".ToCharArray()), arr_1[k]); 47 } 48 } 49 return true; 50 } 51 52 /// <summary> 53 /// 返回一个匹配上的路由对象,如果没有匹配http的请求路径的,则返回一个null. 54 /// </summary> 55 /// <param name="httpContextBase"></param> 56 /// <returns></returns> 57 public override RouteData GetRouteDara(HttpContextBase httpContextBase) 58 { 59 IDictionary<string, object> variables; 60 if (Match(httpContextBase.Request.AppRelativeCurrentExecutionFilePath.Substring(2), out variables)) 61 { 62 RouteData routeData = new RouteData(); 63 foreach (var item in variables) 64 { 65 routeData.Values.Add(item.Key, item.Value); 66 } 67 foreach (var item in DataToken) 68 { 69 routeData.DataTokens.Add(item.Key, item.Value); 70 } 71 routeData.RouteHandler = RouteHandler; 72 73 return routeData; 74 } 75 return null; 76 } 77 } 78 }
MvcRouteHandler类,实现接口IRouteHandler接口,返回一个HttpHandler对象,具体代码如下:
1 namespace WebMvc 2 { 3 public class MvcRouteHandler:IRouteHandler 4 { 5 public IHttpHandler GetHttpHandler(RequestContext requestContext) 6 { 7 return new MvcHandler(requestContext); 8 } 9 } 10 }
同一个web应用可以采用多种不同的URL模式,故需要注册多个继承RouteBase的对象(Route)来解析HTTp请求。多个路由对象组成一个路由表:RouteTable.
/// <summary> /// RouteTable定义指定路由对象的字典,无参构造初始化 /// </summary> public class RouteTable { public RouteDictionary Routes { get; private set; } public RouteTable() { Routes = new RouteDictionary(); } }
1 public class RouteDictionary:Dictionary<string,RouteBase> 2 { 3 public RouteData GetRouteData(HttpContextBase httpContextBase) 4 { 5 //this指代字典,Values是字典的value值 6 foreach (var data in this.Values) 7 { 8 RouteData routeData = data.GetRouteDara(httpContextBase); 9 if (routeData != null) 10 return routeData; 11 } 12 return null; 13 } 14 }