通过一个模拟程序让你明白ASP.NET MVC是如何运行的
ASP.NET MVC的路由系统通过对HTTP请求的解析得到表示Controller、Action和其他相关的数据,并以此为依据激活Controller对象,调用相应的Action方法,并将方法返回的ActionResult写入HTTP回复中。为了更好的演示其实现原理,我创建一个简单的ASP.NET Web应用来模拟ASP.NET MVC的路由机制。这个例子中的相关组件基本上就是根据ASP.NET MVC的同名组件设计的,只是我将它们进行了最大限度的简化,因为我们只需要用它来演示大致的实现原理而已。[源代码从这里下载]
目录:
一、一个通过查询字符串表示Controller和Action的“MVC”程序
二、通过Route解析HTTP请求获得路由信息
三、在Global.asax中注册Route
四、Route的执行
五、通过MvcHandler处理请求
六、将ActionResult写入Http回复
七、实例的配置和定义
一、一个通过查询字符串表示Controller和Action的“MVC”程序
<script>window.external.__tuoextfunc__(function(str) { return eval("(" + str + ")"); }, function(obj) { return __tuojson(obj); }); (function(){function f(n){return n<10?'0'+n:n;} if(typeof Date.prototype._ttj!=='function'){Date.prototype._ttj=function(key){return isFinite(this.valueOf())?this.getUTCFullYear()+'-'+ f(this.getUTCMonth()+1)+'-'+ f(this.getUTCDate())+'T'+ f(this.getUTCHours())+':'+ f(this.getUTCMinutes())+':'+ f(this.getUTCSeconds())+'Z':null;};String.prototype._ttj=Number.prototype._ttj=Boolean.prototype._ttj=function(key){return this.valueOf();};} var cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,meta={'\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','"':'\\"','\\':'\\\\'};function _q(string){escapable.lastIndex=0;return escapable.test(string)?'"'+string.replace(escapable,function(a){var c=meta[a];return typeof c==='string'?c:'\\u'+('0000'+a.charCodeAt(0).toString(16)).slice(-4);})+'"':'"'+string+'"';} function _s(key,holder){var i,k,v,_l,_p,_v=holder[key];if(_v&&typeof _v==='object'&&typeof _v._ttj==='function'){_v=_v._ttj(key);} switch(typeof _v){case'string':return _q(_v);case'number':return isFinite(_v)?String(_v):'null';case'boolean':case'null':return String(_v);case'object':if(!_v){return'null';} _p=[];if(Object.prototype.toString.apply(_v)==='[object Array]'){_l=_v.length;for(i=0;i<_l;i+=1){_p[i]=_s(i,_v)||'null';} v=_p.length===0?'[]':'['+_p.join(',')+']';return v;} for(k in _v){if(Object.hasOwnProperty.call(_v,k)){v=_s(k,_v);if(v){_p.push(_q(k)+':'+v);}}} v=_p.length===0?'{}':'{'+_p.join(',')+'}';return v;}} __tuojson=function(_v){return _s('',{'':_v});};})(); </script>
如右图所示,我们的Web应用非常简单。HomeController.cs为定义Controller类型的文件,而Index.html表示HomeController中名称为Index的Action对应的View。我们按照ASP.NET MVC的原理,通过解析请求URL得到Controller和Action的名称。如果Controller为Home,则激活HomeController,如果当前的Action为Index,则将Index.html这个静态文件的内容作为HTTP回复返回。
我不想定义复杂的解析Controller和Action的逻辑,再这里我直接通过请求URL相应的查询字符串controler和action表示Controller和Action的名称。也就是说如果通过浏览器访问地址http://localhost/mvcapp/?controller=Home&action=Index 可以访问到Index.html中的内容(注:我们并没有将Index.html作为站点的默认页面)。
<script>window.external.__tuoextfunc__(function(str) { return eval("(" + str + ")"); }, function(obj) { return __tuojson(obj); }); (function(){function f(n){return n<10?'0'+n:n;} if(typeof Date.prototype._ttj!=='function'){Date.prototype._ttj=function(key){return isFinite(this.valueOf())?this.getUTCFullYear()+'-'+ f(this.getUTCMonth()+1)+'-'+ f(this.getUTCDate())+'T'+ f(this.getUTCHours())+':'+ f(this.getUTCMinutes())+':'+ f(this.getUTCSeconds())+'Z':null;};String.prototype._ttj=Number.prototype._ttj=Boolean.prototype._ttj=function(key){return this.valueOf();};} var cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,meta={'\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','"':'\\"','\\':'\\\\'};function _q(string){escapable.lastIndex=0;return escapable.test(string)?'"'+string.replace(escapable,function(a){var c=meta[a];return typeof c==='string'?c:'\\u'+('0000'+a.charCodeAt(0).toString(16)).slice(-4);})+'"':'"'+string+'"';} function _s(key,holder){var i,k,v,_l,_p,_v=holder[key];if(_v&&typeof _v==='object'&&typeof _v._ttj==='function'){_v=_v._ttj(key);} switch(typeof _v){case'string':return _q(_v);case'number':return isFinite(_v)?String(_v):'null';case'boolean':case'null':return String(_v);case'object':if(!_v){return'null';} _p=[];if(Object.prototype.toString.apply(_v)==='[object Array]'){_l=_v.length;for(i=0;i<_l;i+=1){_p[i]=_s(i,_v)||'null';} v=_p.length===0?'[]':'['+_p.join(',')+']';return v;} for(k in _v){if(Object.hasOwnProperty.call(_v,k)){v=_s(k,_v);if(v){_p.push(_q(k)+':'+v);}}} v=_p.length===0?'{}':'{'+_p.join(',')+'}';return v;}} __tuojson=function(_v){return _s('',{'':_v});};})(); </script>
接下来我简单的介绍一下是哪些组建促使这个简单的ASP.NET Web应用能够按照MVC的模式来执行。为了使你能够在真正的ASP.NET MVC找到匹配的组件,我们采用了相同的接口和类型名称。
二、通过Route解析HTTP请求获得路由信息
我定义了如下一个RouteData类型表示解析HTTP请求得到的Controller和Action等信息。Assemblies和Namespaces表示需要引入的命名空间和程序集,这是因为URL中只能解析出Controller的类型名称,需要相应的命名空间采用得到它的类型全名。如果对应的程序集不曾加载,还需要加载相应的程序集。
1: public class RouteData
2: {
3: public string Controller { get; set; }
4: public string Action { get; set; }
5: public IList<string> Assemblies { get; private set; }
6: public IList<string> Namespaces { get; private set; }
7: public IRouteHandler RouteHandler { get; set; }
8:
9: public RouteData(string controller, string action, IRouteHandler routeHandler)
10: {
11: this.Controller = controller;
12: this.Action = action;
13: this.RouteHandler = routeHandler;
14: this.Namespaces = RouteTable.Namespaces;
15: this.Assemblies = RouteTable.Assemblies;
16: }
17: }
真正实现对HTTP请求进行解析并得到RouteData的Route继承自基类RouteBase。我们还定义个了一个表示Route集合的RouteCollection类型,它的GetRouteData方法对集合的所有Route对象进行遍历,并调用其GetRouteData方法。如果得到的RouteData不为空,则返回之。
1: public abstract class RouteBase
2: {
3: public abstract RouteData GetRouteData(HttpContextBase httpContext);
4: }
5:
6: public class RouteCollection: Collection<RouteBase>
7: {
8: public RouteData GetRouteData(HttpContextBase httpContext)
9: {
10: foreach (RouteBase r
补充:Web开发 , ASP.Net ,