学习ASP.NET MVC5框架揭秘笔记-ASP.NET MVC是如何运行的(二)
路由
对于一个asp.net mvc应用来说,针对http请求的处理实现在目标controller类型的某个action,每个http请求不在像asp.net web forms应用一样是针对一个物理文件,而是针对某个controller的某个action方法。目标controller和action的名称由http请求的url来决定,当asp.net mvc接收到抵达的请求后,其首要任务就是通过当前http请求解析得到目标controller和action的名称,这个过程是通过asp.net mvc的路由来实现的。我们通过如下几个对象构建了一个简易的路由系统。
1.routedata
asp.net定义了一个全局的路由表,路由表中的每个route对象包含一个路由模板。目标controller和action的名称可以通过路由变量以占位符的形式定义在模板中,也可以作为路由对象的默认值(无须出现在路由模板中)。对于每一个抵达的http请求,路由系统会遍历路由表并找到一个具有与当前请求url模式相匹配的route对象,然后利用它解析出以controller和action名称为核心的路由数据。在我们自建的asp.net mvc框架中,通过路由解析得到的路由数据通过具有如下定义的routedata类型表示。
public class routedata { public idictionary values { get; private set; } public idictionary datatokens { get; private set; } public iroutehandler routehandler { get; set; } public routebase route { get; set; } public routedata() { this.values = new dictionary(); this.datatokens = new dictionary(); this.datatokens.add(namespaces, new list()); } public string controller { get { object controllername = string.empty; this.values.trygetvalue(controller, out controllername); return controllername.tostring(); } } public string actionname { get { object actionname = string.empty; this.values.trygetvalue(action, out actionname); return actionname.tostring(); } } }
routedata定义了两个字典类型的属性values和datatokens,他们代表具有不同来源的路由变量,前者由对请求url实施路由解析获得。表示controller和action名称的属性直接从values属性的字典中提取,对应的key分别为“controller”和“action”。
asp.net mvc的本质是由自定义的httpmodule和自定义的httphandler两个来实现的。httpmodule从routedata对象的routehandler属性获得。routedata的routehandler属性类型为iroutehandler接口,该接口具有一个唯一的gethttphandler方法返回真正真正处理http请求的httphandler对象。该方法有一个类型为requestcontext的参数。requestcontext表示当前(http)请求的上下文,其核心就是对当前httpcontext和routedata的封装。
public interface iroutehandler { ihttphandler gethttphandler(requestcontext requestcontext); } public class requestcontext { public virtual httpcontextbase httpcontext { get; set; } public virtual routedata routedata { get; set; } }
2.route和routetable
承载路由变量的routedata对象由路由表中与当前请求相匹配的route对象生成,可以通过routedata的route属性获得这个route对象,该属性的类型为routebase。如下面的代码片段所示,routebase是一个抽象类,他仅仅包含一个返回类型为routedata的getroutedata方法。
public abstract class routebase { public abstract routedata getroutedata(httpcontextbase httpcontext); }
routebase的getroutedata方法具有一个类型为httpcontextbase的参数,它代表针对当前请求的http上下文。当该方法被执行的时候,它会判断自身定义的路由规则是否与当前请求相匹配,并在成功匹配的情况下实施路由解析,并将得到的路由变量封装成routedata对象返回。如果路由规则与当前请求不匹配,则该方法直接返回null。
我们定义了如下一个继承自routebase的route类型来完成具体的路由工作。一个route对象具有一个代表路由模板的字符串类型的url属性。在实现的getroutedata方法中,我们通过httpcontextbase获取当前请求的url,如果它与路由模板的模式相匹配,则创建一个routedata对象作为返回值。对于返回的routedata对象,其values属性表示的字典对对象包含直接通过解析出来的变量,而对于datatokens字典和routehandler属性,则直接取自route对象的同名属性。
public class route : routebase { public iroutehandler routehandler { get; set; } public string url { get; set; } public idictionary datatokens { get; set; } public route() { this.datatokens = new dictionary(); this.routehandler = new mvcroutehandler(); } public override routedata getroutedata(httpcontextbase httpcontext) { idictionary 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; } protected bool match(string requesturl,out idictionary variables) { variables = new dictionary(); 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({}.tochararray()), strarray1[i]); } } return true; } }
一个web应用可以采用多种不同的url模式,所以需要注册多个继承自routebase的route对象,多个route对象组成了一个路由表。在我们自定义的迷你版asp.net mvc框架中,路由表通过类型routetable表示。routetable仅仅具有一个类型为routedictionary的routes属性表示针对整个web应用的全局路由表。
public class routetable { public static routedictionary routes { get; private set; } static routetable() { routes = new routedictionary(); } }
routedictionary表示一个具名的route对象的列表,我们直接让它继承自泛型的字典类型dictionary
public class routedictionary : dictionary { public routedata getroutedata(httpcontextbase httpcontext) { foreach (var route in this.values) { routedata routedata = route.getroutedata(httpcontext); if (null != routedata) { return routedata; } } return null; } }
在global.asax中我们创建了一个基于指定路由模板的route对象,并将其添加到通过routetable的静态只读属性routes所表示的全局路由表中。
public class global : system.web.httpapplication { protected void application_start(object sender, eventargs e) { routetable.routes.add(default, new route { url = {controller}/{action} }); } }
3.urlroutingmodule
路由表的作用是对当前的http请求实施路由解析,进而得到一个以controller和action名称为核心的路由数据,即上面介绍的routedata对象。整个路由解析工作是通过一个类型为urlroutingmodule的自定义ihttpmodule来完成的。
public class urlroutingmodule : ihttpmodule { public void dispose() { } public void init(httpapplication context) { context.postresolverequestcache += onpostresolverequestcache; } protected virtual void onpostresolverequestcache(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); } }
在实现的init方法中,我们注册了httpapplication的postresolverequestcache事件。当代表当前应用的httpapplication对象的postresolverequestcache事件触发之后,urlroutingmodule通过routetable的静态只读属性routes得到表示全局路由表的routedictionary对象,然后根据当前http上下文创建一个httpcontextwrapper对象(httpcontextwrapper是httpcontextbase的子类),并将其作为参数调用routedictionary对象的getroutedata方法。
如果方法调用返回一个具体的routedata对象,urlroutingmodule会根据该对象本身和之前得到的httpcontextwrapper对象创建一个表示当前上下文的requestcontext对象,并将其作为参数传入routedata的routehandler的gethttphandler方法得到一个httphandler对象。urlroutingmodule最后调用httpcontextwrapper对象的remaphandler方法对得到的httphandler对象进行映射,那么针对当前http请求的后续处理将由这个httphandler来接手。
上一篇: 中秋吃柚子的由来是什么?中秋还吃什么?
下一篇: Vue.js 技术揭秘
推荐阅读
-
学习ASP.NET MVC5框架揭秘笔记-ASP.NET MVC路由(三)
-
学习ASP.NET MVC5框架揭秘笔记-ASP.NET MVC是如何运行的(二)
-
学习ASP.NET MVC框架揭秘笔记-实例演示:SC模式的应用
-
ASP.NET MVC是如何运行的[1]: 建立在“伪”MVC框架上的Web应用
-
学习ASP.NET MVC5框架揭秘笔记-ASP.NET MVC是如何运行的(四)
-
学习ASP.NET MVC5框架揭秘笔记-ASP.NET MVC路由
-
学习ASP.NET MVC5框架揭秘笔记-ASP.NET MVC路由(三)
-
学习ASP.NET MVC5框架揭秘笔记-ASP.NET MVC路由(二)
-
学习ASP.NET MVC5框架揭秘笔记-ASP.NET MVC是如何运行的(一)
-
学习ASP.NET MVC框架揭秘笔记-实例演示:SC模式的应用