通过一个模拟程序让你明白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”程序
如右图所示,我们的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作为站点的默认页面)。
接下来我简单的介绍一下是哪些组建促使这个简单的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 route in this)
11: {
12: var routeData = route.GetRouteData(httpContext);
13: if (null != routeData)
14: {
15: return routeData;
16: }
17: }
18: return null;
19: }
20: }
和ASP.NET MVC一样,我们定义了如下一个RouteTable对象,其静态属性正是一个RouteCollection对象。两个静态属性Namespaces和Assemblies为命名空间和程序集名称的全局维护。
1: public class RouteTable
2: {
3: static RouteTable()
4: {
5: Routes = new RouteCollection();
6: Namespaces = new List<string>();
7: Assemblies = new List<string>();
8: }
9: public static RouteCollection Routes { get; private set; }
10: public static IList<string> Namespaces { get; private set; }
11: public static IList<string> Assemblies { get; private set; }
12: }
而我们实例中完成基于查询字符串的Controller和Action解析的QueryStringRoute对应如下。在GetRouteData方法中,除了根据查询字符解析并初始化Controller和Action名称之外,还将RouteHandler指定为MvcRouteHandler。而MvcRouteHandler得GetHttpHandler方法直接返回的是根据RequestContext创建的MvcHandler对象。
1: public class QueryStringRoute : RouteBase
2: {
3: public override RouteData GetRouteData(HttpContextBase httpContext)
4: {
5: if (httpContext.Request.QueryString.AllKeys.Contains("controller") &&
6: httpContext.Request.QueryString.AllKeys.Contains("controller") )
7: {
8: string controller = httpContext.Request.QueryString["controller"];
9: string action = httpContext.Request.QueryString["action"];
10: IRouteHandler routeHandler = new MvcRouteHandler();
11: return new RouteData(controller, action, routeHandler);
12: }
13: return null;
14: }
15: }
16:
17: public class MvcRouteHandler: IRouteHandler
18: {
19: public IHttpHandler GetHttpHandler(RequestContext requestContext)
20: {
21: return new MvcHandler(requestContext);
22: }
23: }
三、在Global.asax中注册Route
通过上面定义的RouteTable类型,我们在Global.asax中按照如下的方式在应用启动的时候QueryStringRoute对象添加到RouteTable的静态属性Routes表示的Route列表中。同时为需要的命名空间和程序集名称进行初始化,以辅助后续步骤中对Controller的创建。
1: public class Global : System.Web.HttpApplication
2: {
3: protected void Application_Start(object sender, EventArgs e)
4: {
5: RouteTable.Routes.Add(new QueryStringRoute());
6: RouteTable.Assemblies.Add("MvcApp, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");
7: RouteTable.Namespaces.Add("Artech.MvcApp");
8: }
9: }
四、Route的执行
通过RouteTable的Routes属性表示的Route列表对请求的解析和路由信息的获取是通过自定义的HttpModule来实现的,它的类型为UrlRoutingModule。如下面的代码片断所示,UrlRoutingModule注册了HttpApplication的PostResolveRequestCache事件,并在该事件触发的时候调用Route列表的GetRouteData方法,并根据得到RouteData创建RequestContext。最后通过RouteData的RouteHandler得到真正用于处理该请求的HttpHandler对象,并对其进行映射。这意味着后续将会采用这个映射的HttpHandler进行请求的处理。
1: public class UrlRoutingModule: IHttpModule
2: {
3: public void Dispose() { }
4: public void Init(HttpApplication context)
5: {
6: context.PostResolveRequestCache += (sender, args) =>
7: {
8: HttpContextWrapper contextWrapper = new HttpContextWrapper(context.Context);
9: HttpContextBase httpContext = (HttpContextBase)contextWrapper;
10: RouteData routeData = RouteTable.Routes.GetRouteData(httpContext);
11: if (null == routeData)
12: {
13: return;
14: }
15: RequestContext requestContext = new RequestContext { HttpContext = httpContext, RouteData = routeData };
16: httpContext.RemapHandler(routeData.RouteHandler.GetHttpHandler(requestContext));
17: };
18: }
19: }
五、通过MvcHandler处理请求
在UrlRoutingModule映射的实际上是具有如下定义的MvcHandler,它具有一个RequestContext属性通过构造函数进行初始化。在ASP.NET MVC中,真正的请求处理体现在根据路由信息创建Controller,并执行相应的Action方法。这两个步骤体现的ProcessRequest方法中。
1: public class MvcHandler: IHttpHandler
2: {
3: public RequestContext RequestContext{get; private set;}
4: public IControllerFactory ControllerFactory
5: {
6: get { return ControllerBuilder.Current.GetControllerFactory(); }
7: }
8: public MvcHandler(RequestContext requestContext)
9: {
10: this.RequestContext = requestContext;
11: }
12: public bool IsReusable
13: {
14: get { return false; }
15: }
16: public void ProcessRequest(HttpContext context)
17: {
18: RouteData routeData = this.RequestContext.RouteData;
19: var controller = this.ControllerFactory.CreateController(this.RequestContext, routeData.Controller);
20: controller.Execute(this.RequestContext);
21: }
22: }
Controller实现了具有如下定义的接口IController,所有Action方法都通过Execute方法执行,该方法的参数的表示当前请求上下文的RequestContext对象。IController通过相应的Controller工厂创建,下面的代码同时也定义了Controller工厂接口的定义。
1: public interface IController
2: {
3: void Execute(RequestContext requestContext);
4: }
5: public interface IControllerFactory
6: {
7: IController CreateController(RequestContext requestContext, string controllerName);
8: }
我们定义了如下一个简单名称为DefaultController,它的Execute方法定义很简单:通过包含在RequestContext的RouteData得到当前的Action,并将它作为方法名得到相应的MethodInfo对象,滨个通过反射调用它得到一个ActionResult对象,最后执行ActionResult的ExecuteResult方法。该方法的参数是基于RequestContext创建的另一个上下文ControllerContext。
1: public class DefaultController : IController
2: {
3: public void Execute(RequestContext requestContext)
4: {
5: string action = requestContext.RouteData.Action;
6: MethodInfo method = this.GetType().GetMethod(action);
7: ActionResult result = (ActionResult)method.Invoke(this, null);
8: ControllerContext controllerContext = new ControllerContext
9: {
10: RequestContext = requestContext
11: };
12: result.ExecuteResult(controllerContext);
13: }
14: }
我们定义了具有如下定义的Controller工厂类DefaultControllerFactory。创建Controller的逻辑也不复杂:通过RouteData表示的Controller名称得到相应的Controller类型,通过反射创建Controller对象。由于RouteData中只包含Controller的名称,所以需要通过命名空间和程序集的辅助才能解析出真正的类型。
1: class DefaultControllerFactory : IControllerFactory
2: {
3: public IController CreateController(RequestContext requestContext, string controllerName)
4: {
5: RouteData routeData = requestContext.RouteData;
6: string controllerType = string.Format("{0}Controller", controllerName);
7: IController controller;
8: controller = this.CreateControler(controllerType);
9: if (null != controller)
10: {
11: return controller;
12: }
13: foreach (string assembly in routeData.Assemblies)
14: {
15: controller = this.CreateControler(controllerType, assembly);
16: if (null != controller)
17: {
18: return controller;
19: }
20:
21: foreach (string ns in routeData.Namespaces)
22: {
23: controllerType = string.Format("{0}.{1}Controller", ns, controllerName);
24: controller = this.CreateControler(controllerType, assembly);
25: if (null != controller)
26: {
27: return controller;
28: }
29: }
30: }
31:
32: throw new InvalidOperationException("Cannot locate the controller");
33: }
34: private IController CreateControler(string controllerType, string assembly = null)
35: {
36: Type type = null;
37: if (null == assembly)
38: {
39: type = Type.GetType(controllerType);
40: }
41: else
42: {
43: type = Assembly.Load(assembly).GetType(controllerType);
44: }
45: if (null == type)
46: {
47: return null;
48: }
49: return Activator.CreateInstance(type) as IController;
50: }
51: }
六、将ActionResult写入Http回复
Controller的Action方法的返回值为具有如下定义的ActionResult类型,通过ExecuteResult方法将相应的执行结果写入HTTP回复中。我定义了如下一个StaticViewResult,它根据RouteData中的Action信息找到匹配的.html静态文件,并将文件的内容写入HttpResponse。
1: public abstract class ActionResult
2: {
3: public abstract void ExecuteResult(ControllerContext context);
4: }
5:
6: public class StaticViewResult: ActionResult
7: {
8: public override void ExecuteResult(ControllerContext context)
9: {
10: context.RequestContext.HttpContext.Response.WriteFile(context.RequestContext.RouteData.Action + ".html");
11: }
12: }
七、实例的配置和定义
在我们的实例中定义的HomeController定义如下,在表示Action的Index方法中,直接返回一个StaticViewResult对象。
1: public class HomeController : DefaultController
2: {
3: public ActionResult Index()
4: {
5: return new StaticViewResult();
6: }
7: }
然后在配置中进行了针对UrlRoutingModule的注册,仅此而已。
1: <configuration>
2: <system.webServer>
3: <modules>
4: <add name="UrlRoutingModule" type="Artech.MvcRouting.UrlRoutingModule, Artech.MvcRouting"/>
5: </modules>
6: </system.webServer>
7: </configuration>
通过一个模拟程序让你明白ASP.NET MVC是如何运行的的更多相关文章
- 通过一个模拟程序让你明白WCF大致的执行流程
原文http://www.cnblogs.com/artech/archive/2011/12/07/wcf-how-to-work.html 在<通过一个模拟程序让你明白ASP.NET MVC ...
- 仅此一文让你明白ASP.NET MVC 之View的显示(仅此一文系列二)
题外话 一周之前写的<仅此一文让你明白ASP.NET MVC原理>受到了广大学习ASP.NET MVC同学的欢迎,于是下定决心准备把它写成一个系列,以满足更多求知若渴的同学们.蒋金楠老师已 ...
- 仅此一文让你明白ASP.NET MVC 之Model的呈现(仅此一文系列三)
本文目的 我们来看一个小例子,在一个ASP.NET MVC项目中创建一个控制器Home,只有一个Index: public class HomeController : Controller { pu ...
- 仅此一文让你明白ASP.NET MVC 之Model的呈现
本文目的 我们来看一个小例子,在一个ASP.NET MVC项目中创建一个控制器Home,只有一个Index: public class HomeController : Controller { pu ...
- 仅此一文让你明白ASP.NET MVC原理
ASP.NET MVC由以下两个核心组成部分构成: 一个名为UrlRoutingModule的自定义HttpModule,用来解析Controller与Action名称: 一个名为MvcHandler ...
- 【转】仅此一文让你明白ASP.NET MVC原理
原文地址:http://www.cnblogs.com/DotCpp/p/3269043.html ASP.NET MVC由以下两个核心组成部分构成: 一个名为UrlRoutingModule的自定义 ...
- ASP.NET没有魔法——ASP.NET MVC是如何运行的?它的生命周期是什么?
前面的文章我们使用ASP.NET MVC创建了个博客应用,那么它是如何工作的呢?我们都知道ASP.NET的程序需要部署到IIS上才能够通过浏览器来访问,那么IIS与ASP.NET MVC程序之间又是如 ...
- ASP.NET开发实战——(四)ASP.NET MVC是如何运行的?它的生命周期是什么?
前面的文章我们使用ASP.NET MVC创建了个博客应用,那么它是如何工作的呢?我们都知道ASP.NET的程序需要部署到IIS上才能够通过浏览器来访问,那么IIS与ASP.NET MVC程序之间又是如 ...
- 仅此一文让你明白ASP.NET MVC 之View的显示
有些人要问题,为什么我要学框架?这里我简单说一下,深入理解一个框架,给你带来最直接的好处: 使用框架时,遇到问题可以快速定位,并知道如何解决: 当框架中有些功能用着不爽时,你可以自由扩展,实现你想要的 ...
随机推荐
- TaintDroid剖析之File & Memiry & Socket级污点传播
TaintDroid剖析之File & Memiry & Socket级污点传播 作者:简行.走位@阿里聚安全 1.涉及到的代码文件 TaintDroid在File, Memory以及 ...
- 安卓动态调试七种武器之孔雀翎 – Ida Pro
安卓动态调试七种武器之孔雀翎 – Ida Pro 作者:蒸米@阿里聚安全 0x00 序 随着移动安全越来越火,各种调试工具也都层出不穷,但因为环境和需求的不同,并没有工具是万能的.另外工具是死的,人是 ...
- 接口自动化测试的"开胃小菜"---简单黑客攻击手段
Web应用系统的小安全漏洞及相应的攻击方式 接口自动化测试的"开胃小菜" 1 写作目的 本文讲述一个简单的利用WebAPI来进行一次基本没有破坏力的“黑客”行为. 主要目的如下 ...
- 分辨率、DPI、PPI和屏幕尺寸,你都知道是啥么?
分辨率.DPI.PPI和屏幕尺寸 分辨率 DPI/PPI 坑爹的屏幕尺寸 Reference 手机开发中不免会遇到分辨率.DPI.PPI和屏幕尺寸等术语,那就弄弄清楚这些概念的真正含义. 分辨率 分辨 ...
- 跨域资源共享(CORS)在ASP.NET Web API中是如何实现的?
在<通过扩展让ASP.NET Web API支持W3C的CORS规范>中,我们通过自定义的HttpMessageHandler自行为ASP.NET Web API实现了针对CORS的支持, ...
- windows命令——explorer
转至http://www.cnblogs.com/ymind/archive/2012/03/30/explorer-command-args.html 今天才知道,explorer原来可以这样用, ...
- 摇钱树运营小工具UI设计.vsd
去年,我负责公司的一个互联网投融资平台——摇钱树.系统运营过程中,业务和客服那边不断的反馈一些事情让技术这边协助实现.例如,土豪客户忘记登录密码后懒得自己重置,更愿意选择搭讪客服MM:再比如,客户多次 ...
- 2013 duilib入门简明教程 -- 前言(1)
关于duilib的介绍就不多讲了,一来不熟,二来小伙伴们想必已经对比了多个界面库,也无需赘述.下面进入正题: 不看广告看疗效! 已有众多知名公司采用duilib做为界面库,如华为网盘. ...
- Spring<bean>标签是反射来实现的
- Highchart.js
Highcharts所有的源代码及例子均可通过官网下载得到.初学者只需要简单的修改官方提供的例子即可了解Highcharts. 下载 最新版本:Highcharts 3.0.10.Highstock ...