ASP.NET MVC 可以简单的归结为两个步骤:
1、根据请求的URL找到一个Controller的Action来作为响应。
2、Action执行后生成一个ActionResult,这个ActionResult最终形成响应的内容。
这二者 连接的执行逻辑为:
System.Web.Mvc.MvcHandler.ProcessRequest(HttpContextBase) 调用 controller.Execute(this.RequestContext),调用
controller.ActionInvoker.InvokeAction(base.ControllerContext, requiredString)调用 controller.ActionInvoker.InvokeActionResult(controllerContext, context.Result); 或者 controller.ActionInvoker.InvokeActionResultWithFilters(controllerContext, filters.ResultFilters, context2.Result); 调用
actionResult.ExecuteResult(controllerContext);
MvcHandler.ProcessRequest方法执行时,会调用controller.Execute,从而激活Controller的Action方法,而Action执行后,会返回一个ActionResult,这个ActionResult的ExecuteResult方法接着被调用,后面的任务就交给了这个ActionResult的ExecuteResult方法,此方法最终完成向缓冲区写入内容。
1、Controller的某个Action方法执行后,会返回一个ActionResult,这个ActionResult类只有一个方法:ExecuteResult。
2、ActionResult大体上可以分为两类,即ViewResult和非ViewResult,二者的唯一区别是一个有IView属性,而另一个没有。 3、非ViewResult的ExecuteResult方法会直接执行response.Write来向响应缓冲区写入内容,而ViewResult是利用其View(IView类型)来向响应缓冲区写入内容。 4、ViewResult首先调用其FindView方法来找到一个View,然后调用此View的Render方法来向响应缓冲区写入内容。
5、ViewResult的FindView方法实际上最终调用了IViewEngine的FindView来找到一个具体的View,如WebFormView和RazorView而这两种View的Render方法分别利用ViewPage和WebViewPage来完成生产Html的工作。
1
IView 只有一个方法:void Render(ViewContext viewContext, TextWriter writer); IController也只有一个方法:void Execute(RequestContext requestContext);
一般来说,IController执行后返回的都是ActionResult对象,ActionResult有JavaScriptResult、FileResult、ViewResult等子类,显然,从这里可以看出Controller里放置的是业务逻辑代码,而刚性的底层通用功能,则是由ActionResult的子类完成,这些子类就是主要完成类似response.Write或者response.OutputStream.Write这样的功能(除了ViewResult),业务程序员不用关心这些功能,由工具程序员搞定。总结一下就是Controller负责业务逻辑,而ActionResult负责Http协议实现中的Web交互逻辑,很明显是一种职责分离的设计。
ActionResult就是一个方法:public abstract void ExecuteResult(ControllerContext context); 其子类实现中大多有类似这样的代码:response.Write或者response.OutputStream.Write。
姑且将ActionResult分为两类:ViewResult和非ViewResult。对于非ViewResult类型的ActionResult的意义很
2
明显,就是完成类似response.Write或者response.OutputStream.Write这样的功能,显然ASP.NET MVC的主要设计功能在于ViewResult类。
3
4
看看ViewResultBase.ExecuteResult(ControllerContext context)方法的实现: ViewEngineResult viewEngineResult = null; // 看看自己的View属性是否为空 if (this.View == null) { }
TextWriter output = context.HttpContext.Response.Output;
ViewContext viewContext=new ViewContext(context,this.View,this.ViewData,this.TempData, output); // 调用View的Render方法来呈现视图。 this.View.Render(viewContext, output); // 如果viewEngineResult不为空,就是释放掉 if (viewEngineResult != null) { }
从上面的代码可以看出ViewResultBase的主要功能在于呈现视图(的提纲式逻辑),而其子类ViewResult主要职责是发现视图FindView,进一步查看ViewResult实现的FindView方法,可以得知,又将发现视图的逻辑委派给了ViewResultBase.ViewEngineCollection.FindView
方法,而
ViewResultBase.ViewEngineCollection
的值为
ViewEngines.Engines,也即ViewResultBase.ViewEngineCollection.FindView有ViewEngines.Engines.FindView实现。 ViewEngines.Engines.FindView方法调用IViewEngine.FindView来实现其功能,也就是说,最终查找视图的功能是由实
viewEngineResult.ViewEngine.ReleaseView(context, this.View);
// 为空的话,就执行FindView方法,得到一个ViewEngineResult对象,再从此对象中得到View viewEngineResult = this.FindView(context); this.View = viewEngineResult.View;
5
现IViewEngine接口的类来实现的。
查找视图的查找结果是返回一个ViewEngineResult实例。 IViewEngine的功能是发现视图和释放视图。 IView的功能是呈现视图。
ViewResult调用IViewEngine.FindView来得到一个IView,然后利用IView来呈现HTML。FindView在VirtualPathProviderViewEngine中实现,在FindView方法中调用了CreateView这个抽象方法,其子类实现了CreateView方法,分别创建RazorView和WebFormView。 IView.Render方法实现HTML的生成。
RazorView利用WebViewPage来生成HTML。
WebFormView利用ViewPage来生成HTML,而ViewPage继承于System.Web.UI.Page。
6
RouteCollectionExtensions
public static class RouteCollectionExtensions
Name: System.Web.Mvc.RouteCollectionExtensions Assembly: System.Web.Mvc, Version=3.0.0.0
RouteBase:
GetRouteData
Route的属性和方法:
public abstract RouteData GetRouteData(HttpContextBase httpContext);
public abstract VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values);
public RouteValueDictionary Constraints { [CompilerGenerated, TargetedPatchingOptOut(} public RouteValueDictionary DataTokens { [CompilerGenerated, } public RouteValueDictionary Defaults { [} public IRouteHandler RouteHandler { [} public string Url { get; set; }
RouteData的属性和方法:
public RouteBase Route { }
public IRouteHandler RouteHandler { }
public RouteValueDictionary DataTokens { } public RouteValueDictionary Values { }
public string GetRequiredString(string valueName);
ASP.NET定义了一个全局的路由表,在ASP.NET应该程序启动时,要注册本应用程序所有可能的路由,即在Application_Start中使用RouteTable.Routes.MapRoute方法来注册路由。注册路由时有两个关键参数,一个是路由名称,另一个是Route对象,用构造函数创建一个Route对象时,也有两个重要参数,而一个是Url模式字符串,另一个
7
是IRouteHandler对象,其中IRouteHandler接口的对象常见的有MvcRouteHandler、PageRouteHandler、StopRoutingHandler等,所以Route对象中有RouteHandler这个重要对象,同时Route对象还有GetRouteData这个重要方法,来创建一个RouteData对象,RouteData的构造函数参数为Route和RouteHandler。 如果GetRouteData方法返回null,表示当前请求的Url与此路由不匹配。
路由表有个GetRouteData方法(其实在RouteCollection中),会遍历所有路由,看看能不能得到一个RouteData,得到的话,表示当前请求的Url与注册的某个Url模式匹配。
UrlRoutingModule中会根据当前请求,查看注册的全局路由表能否获取到一个RouteData,如果有的话,就会利用此RouteData对象的RouteHandler,获取到一个IHttpHandler,从而将请求处理交给此IHttpHandler对象。
访问Route对象的信息是通过RouteData对象来达到的,Route类有GetRouteData方法,来返回一个RouteData对象,这个RouteData对象包含了所有路由的信息和处理这个路由的处理器信息。
RouteData定义了两个字典类型的属性Values和DataTokens,前者代表直接从请求地址解析出来的变量,后者代表其他类型的变量。表示Controller和Action名称的同名属性直接从Values字典中提取,对应的Key分别为controller和action。属性Namespaces表示辅助Controller类型的解析而设置的命名空间列表,该属性值从DataTokens字典中提取,对应的Key为namespaces。其实RouteData对象的Values属性表示的字典包含直接通过地址解析出来的变量,而对于DataTokens字典和RouteHandler属性,则直接取自Route对象的同名属性。
在ASP.NET路由机制中,有一个RequestContext对象,她是HttpContext和 RouteData对象组合体。
HttpContext 注册路由时为Route,并携带 RouteHandler RouteData Values RequestContext DataTokens Controller ControllerContext ViewData View TempData ViewContext
TextWriter 8
ViewDataDictionary类有Model属性,此属性在Controller.View(model)方法执行时被初始化。 Controller.ViewData.Model = model;
关于URL
URL代表的是Web上的一个资源。常常有多种表现形式: 1、磁盘上存在的一个物理文件。
2、映射到类(Controller)上的一个方法(Action)。 URL重写
将原始url映射(或者说转成)另一个新的url,Web server针对这个新的URL进行响应。 ASP.NET路由
将url映射到Web上的一个资源,该资源往往上一段代码,这段代码动态生成响应。
路由(Route)包含URL模式、路由名称和路由的Handler三者。 URL模式中描述了URL段、每个段的URL参数和分隔符。真实的URL请求提供了这些参数的具体值。路由定义时指明了RouteValueDictionary的Key(参数名)和Key的默认值,并预定义了两个url参数名称:controller和action,,而真实Url提供了这些Key的具体值。
1、应用程序运行之初,将Url模式映射到某个具体的Handler。(或者说将来Url与Handler直接的映射) 2、UrlRoutingModule会拦截一切请求,并分配恰当的handler(根据上一步的映射关系).对于MVC请求,会分配给MvcHandler。
3、MvcHandler创建Controller实例,随之建立ControllerContext实例,并让Controller执行。 4、Controller找到相应的Action方法,并组织Action的参数,执行这个Action。
5、典型地,Controller执行Action方法时,会调用RenderView()方法来向浏览器传说内容,Controller.RenderView() 方法将其他工作委托给特定的ViewEngine来完成。
RouteData对象,其Values属性表示的字典包含直接通过地址解析出来的变量,而对于DataTokens字典和RouteHandler属性,则直接取自Route对象的同名属性。
1、应用程序注册UrlRoutingModule模块,此模块在PostResolveRequestCache阶段发生作用。
2、在Application_Start中注册路由。在注册路由时,会指明路由的模式和处理此路由的RouteHandler为MvcRouteHandler或者PageRouteHandler。
3、当程序运行到PostResolveRequestCache阶段,程序会从RouteTable的静态属性Routes代表的全局路由表中 依据请求的url进行匹配,并得到一个RouteData对象。
RouteData对象中有RouteHandler,当为ASP.NET MVC时,此RouteHandler为MvcRouteHandler,MvcRouteHandler的 GetHttpHandler方法会返回一个IHttpHandler,即MvcHandler。
同时如果没有设置ControllerFactory的话,会利用ControllerBuilder.Current.GetControllerFactory()得到一个默认的ControllerFactory,即 DefaultControllerFactory。 4、MvcHandler执行ProcessRequest方法,此方法首先通过ControllerBuilder.Current.GetControllerFactory()得到ControllerFactory。
string controllerName = this.RequestContext.RouteData.GetRequiredString(\"controller\"); Type controllerType = this.GetControllerType(requestContext, controllerName);
IController controller = this.ControllerActivator.Create(requestContext, controllerType);
ControllerFactory.CreateController方法会利用controllerName和requestContext得到当前要求的Controller
9
实例。
然后controller.Execute(this.RequestContext); 最后 factory.ReleaseController(controller);
public virtual void PostResolveRequestCache(HttpContextBase context) {
RouteData routeData = RouteTable.Routes.GetRouteData(context); IRouteHandler routeHandler = routeData.RouteHandler; // 交给asp.net的路由机制来处理请求
RequestContext requestContext = new RequestContext(context, routeData); context.Request.RequestContext = requestContext;
IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);
context.RemapHandler(httpHandler); }
public static void RegisterRoutes(RouteCollection routes) {
// 可以忽略的路由配置
routes.IgnoreRoute(\"{resource}.axd/{*pathInfo}\"); // 设置默认路由 routes.MapRoute(
\"Default\路由名称
\"{controller}/{action}/{id}\带有参数的 URL
new { controller = \"Home\参数默认值 ); }
protected void Application_Start() {
AreaRegistration.RegisterAllAreas(); RegisterRoutes(RouteTable.Routes); }
System.Web.Mvc.ViewResultBase.ExecuteResult(ControllerContext context) TextWriter output = context.HttpContext.Response.Output;
ViewContext viewContext = new ViewContext(context, this.View, this.ViewData, this.TempData, output); this.View.Render(viewContext, output);
10
因篇幅问题不能全部显示,请点此查看更多更全内容