首先我们知道http
是一种无状态的请求,他的生命周期就是从客户端浏览器发出请求开始,到得到响应结束。那么MVC应用程序从发出请求到获得响应,都做了些什么呢?
本文我们会详细讨论MVC应用程序一个请求的生命周期,从一个控件到另一个控件是怎样被处理的。我们还会详细介绍一下整个请求的生命周期中,用到的相关组件。因为在平常的开发过程中,我们可能知道怎样去使用MVC框架来处理相关的请求,大部分的时候我们只是在controller
和action
方法之间做相关的处理,对于真正内在的运行机制可能不是很了解。其实当我们对内在机制有了一定的了解以后,会发现微软的MVC框架的扩展性很强,到处都留有扩展接口,让我们通过扩展能够自己定义自己所需要的处理机制,这也正是为什么MVC框架如此出名的原因。
当我最开始学习使用mvc的时候,困扰我的一个问题就是,一个请求的流程控制是怎样的呢?从view
到controller
再到action
之间经历了什么?那个时候我还不清楚HTTP module
和HTTP handler
在处理一个请求中扮演什么样的角色,起什么样的作用呢。毕竟MVC是一个web开发框架,在整个请求处理过程中,肯定包含了http module
和http handler
。其实还有很多相关的组件包含在一个完整的mvc应用程序请求生命周期里,在整个请求过程中他们都扮演者非常重要的角色。尽管大部分时候我们都使用的是框架提供的默认的函数,但是如果我们了解了每个控件所扮演的角色,我们就可以轻松的扩展和使用我们自己实现的方法,就目前来说MVC是扩展性比较强的框架。下面是本章节的主要内容:
HttpApplication
HttpModule
HttpHandler
ASP.NET MVC运行机制
UrlRoutingModule
RouteHandler
MvcHandler
ControllerFactory
Controller
ActionInvoker
ActionResult
ViewEngine
HttpApplication
我们都知道,在ASP.NET MVC框架出现之前,我们大部分开发所使用的框架都是ASP.NET WebForm
.其实不管是MVC还是WebForm
,在请求处理机制上,大部分是相同的。这涉及到IIS
对请求的处理,涉及的知识较多,我们就不做介绍了,下次有机会我写一篇专文。我们从HttpApplication
说起。先看看微软官方是怎么定义HttpApplication
的:
定义 ASP.NET 应用程序中的所有应用程序对象共有的方法、属性和事件。此类是用户在
Global.asax
文件中所定义的应用程序的基类。
微软官方文档中Remark
里有这么一段话:HttpApplication
类的实例是在 ASP.NET
基础结构中创建的,而不是由用户直接创建的。使用 HttpApplication
类的一个实例来处理其生存期中收到的众多请求。但是,它每次只能处理一个请求。这样,成员变量才可用于存储针对每个请求的数据。
意思就是说ASP.NET
应用程序,不管是MVC
还是WebForm
,最终都会到达一个HttpApplication
类的实例。HttpApplication
是整个ASP.NET
基础架构的核心,负责处理分发给他的请求。HttpApplication
处理请求的周期是一个复杂的过程,在整个过程中,不同阶段会触发相映的事件。我们可以注册相应的事件,将处理逻辑注入到HttpApplication
处理请求的某个阶段。在HttpApplication
这个类中定义了19个事件来处理到达HttpApplication
实例的请求。就是说不管MVC还是WebForm
,最终都要经过这19个事件的处理,那么除了刚才说的MVC
和WebFrom
在请求处理机制上大部分都是相同的,不同之处在哪呢?他们是从哪里开始分道扬镳的呢?我们猜想肯定就在这19个方法中。我们继续往下看。
我们来看看这19个事件:
应用程序按照以下顺序执行由 global.asax
文件中定义的模块或用户代码处理的事件:
事件名称: | 简单描述: |
BeginRequest | 在 ASP.NET 响应请求时作为 HTTP 执行管线链中的第一个事件发生 |
AuthenticateRequest | 当安全模块已建立用户标识时发生。注:AuthenticateRequest 事件发出信号表示配置的身份验证机制已对当前请求进行了身份验证。预订 AuthenticateRequest 事件可确保在处理附加的模块或事件处理程序之前对请求进行身份验证 |
PostAuthenticateRequest | 当安全模块已建立用户标识时发生。PostAuthenticateRequest 事件在 AuthenticateRequest 事件发生之后引发。预订 PostAuthenticateRequest 事件的功能可以访问由 PostAuthenticateRequest 处理的任何数据 |
AuthorizeRequest | 当安全模块已验证用户授权时发生。AuthorizeRequest 事件发出信号表示 ASP.NET 已对当前请求进行了授权。预订 AuthorizeRequest 事件可确保在处理附加的模块或事件处理程序之前对请求进行身份验证和授权 |
PostAuthorizeRequest | 在当前请求的用户已获授权时发生。PostAuthorizeRequest 事件发出信号表示 ASP.NET 已对当前请求进行了授权。预订PostAuthorizeRequest 事件可确保在处理附加的模块或处理程序之前对请求进行身份验证和授权 |
ResolveRequestCache | 当 ASP.NET 完成授权事件以使缓存模块从缓存中为请求提供服务时发生,从而跳过事件处理程序(例如某个页或 XML Web services)的执行 |
PostResolveRequestCache | 在 ASP.NET 跳过当前事件处理程序的执行并允许缓存模块满足来自缓存的请求时发生。)在 PostResolveRequestCache 事件之后、PostMapRequestHandler 事件之前创建一个事件处理程序(对应于请求 URL 的页 |
PostMapRequestHandler | 在 ASP.NET 已将当前请求映射到相应的事件处理程序时发生。 |
AcquireRequestState | 当 ASP.NET 获取与当前请求关联的当前状态(如会话状态)时发生。 |
PostAcquireRequestState | 在已获得与当前请求关联的请求状态(例如会话状态)时发生。 |
PreRequestHandlerExecute | 恰好在 ASP.NET 开始执行事件处理程序(例如,某页或某个 XML Web services)前发生。 |
PostRequestHandlerExecute | 在 ASP.NET 事件处理程序(例如,某页或某个 XML Web service)执行完毕时发生。 |
ReleaseRequestState | 在 ASP.NET 执行完所有请求事件处理程序后发生。该事件将使状态模块保存当前状态数据。 |
PostReleaseRequestState | 在 ASP.NET 已完成所有请求事件处理程序的执行并且请求状态数据已存储时发生。 |
UpdateRequestCache | 当 ASP.NET 执行完事件处理程序以使缓存模块存储将用于从缓存为后续请求提供服务的响应时发生。 |
PostUpdateRequestCache | 在 ASP.NET 完成缓存模块的更新并存储了用于从缓存中为后续请求提供服务的响应后,发生此事件。 |
LogRequest | 在 ASP.NET 完成缓存模块的更新并存储了用于从缓存中为后续请求提供服务的响应后,发生此事件。 |
仅在 IIS 7.0 处于集成模式并且 .NET Framework 至少为 3.0 版本的情况下才支持此事件 | |
PostLogRequest | 在 ASP.NET 处理完 LogRequest 事件的所有事件处理程序后发生。 |
仅在 IIS 7.0 处于集成模式并且 .NET Framework 至少为 3.0 版本的情况下才支持此事件。 | |
EndRequest | 在 ASP.NET 响应请求时作为 HTTP 执行管线链中的最后一个事件发生。 |
在调用 CompleteRequest 方法时始终引发 EndRequest 事件。 |
对于一个ASP.NET
应用程序来说,HttpApplication
派生与Global.aspx
(可以看看我们创建的应用程序都有一个Global.aspx
文件),我们可以在Global.aspx
文件中对HttpApplication
的请求进行定制即注入这19个事件中的某个事件进行逻辑处理操作。在Global.aspx
中我们按照"Application_{Event Name}
"这样的方法命名进行事件注册。
Event Name
就是上面19个事件的名称。比如Application_EndRequest
就用于处理Application
的EndRequest
事件。
HttpModule
ASP.NET拥有一个高度可扩展的引擎,并且能够处理对于不同资源类型的请求。这就是HttpModule
。当一个请求转入ASP.net管道时,最终负责处理请求的是与资源相匹配的HttpHandler
对象,但是在HttpHandler
进行处理之前,ASP.NET先会加载并初始化所有配置的HttpModule
对象。HttpModule
初始化的时候,会将一些回调事件注入到HttpApplication
相应的事件中。所有的HttpModule
都实现了IHttpModule
接口,该接口有一个有一个Init
方法。
public interface IHttpModule
{
// Methods
void Dispose();
void Init(HttpApplication context);
}
看到Init
方法呢接受一个HttpApplication
对象,有了这个对象就很容易注册HttpApplication
中19个事件中的某个事件了。这样当HttpApplication
对象执行到某个事件的时候自然就会出发。
HttpHandler
对于不同的资源类型的请求,ASP.NET会加载不同的HttpHandler
来处理。所有的HttpHandler
都实现了IhttpHandler
接口。
public interface IHttpHandler
{
// Methods
void ProcessRequest(HttpContext context);
// Properties
bool IsReusable { get; }
}
我们看到该接口有一个方法ProcessRequest
,顾名思义这个方法就是主要用来处理请求的。所以说每一个请求最终分发到自己相应的HttpHandler
来处理该请求。
ASP.NET MVC 运行机制
好了,上面说了那么多,其实都是给这里做铺垫呢。终于到正题了。先看看下面这张图,描述了MVC的主要经历的管道事件:
上图就是一个完整的mvc应用程序的一个http请求到响应的整个儿所经历的流程。从UrlRoutingModule
拦截请求到最终ActionResult
执行ExecuteResult
方法生成响应。
下面我们就来详细讲解一下这些过程都做了些什么。
UrlRoutingModule
MVC应用程序的入口UrlRoutingModule
首先发起一个请求,我们前面讲到ASP.NET 会加载一个HttpModule
对象的初始化事件Init
,而所有的HttpModule
对象都实现了IHttpModule
接口。我们看看UrlRoutingModule
的实现:
从上图中我们看到UrlRoutingModule
实现了接口IHttpModule
,当一个请求转入ASP.NET
管道时,就会加载 UrlRoutingModule
对象的Init()
方法。
那么为什么偏偏是UrlRoutingModule
被加载初始化了呢?为什么不是别的HttpModule
对象呢?带着这个疑问我们继续。
在ASP.NET MVC中,最核心的当属“路由系统”,而路由系统的核心则源于一个强大的System.Web.Routing.dll
组件。System.Web.Routing.dll
不是MVC所特有的,但是MVC框架和它是密不可分的。
首先,我们要了解一下UrlRoutingModule
是如何起作用的。
(1)IIS网站的配置可以分为两个块:全局 Web.config
和本站 Web.config。Asp.Net Routing
属于全局性的,所以它配置在全局Web.Config
中,我们可以在如下路径中找到:“C\Windows\Microsoft.NET\Framework\版本号\Config\Web.config
“,我提取部分重要配置大家看一下:
<httpModules>
<add name="OutputCache" type="System.Web.Caching.OutputCacheModule" />
<add name="Session" type="System.Web.SessionState.SessionStateModule" />
<add name="WindowsAuthentication" type="System.Web.Security.WindowsAuthenticationModule" />
<add name="FormsAuthentication" type="System.Web.Security.FormsAuthenticationModule" />
<add name="PassportAuthentication" type="System.Web.Security.PassportAuthenticationModule" />
<add name="RoleManager" type="System.Web.Security.RoleManagerModule" />
<add name="UrlAuthorization" type="System.Web.Security.UrlAuthorizationModule" />
<add name="FileAuthorization" type="System.Web.Security.FileAuthorizationModule" />
<add name="AnonymousIdentification" type="System.Web.Security.AnonymousIdentificationModule" />
<add name="Profile" type="System.Web.Profile.ProfileModule" />
<add name="ErrorHandlerModule" type="System.Web.Mobile.ErrorHandlerModule, System.Web.Mobile, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<add name="ServiceModel" type="System.ServiceModel.Activation.HttpModule, System.ServiceModel.Activation, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
<add name="UrlRoutingModule-4.0" type="System.Web.Routing.UrlRoutingModule" />
<add name="ScriptModule-4.0" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
</httpModules>
大家看到没有,我上面标红的那一行:<add name="UrlRoutingModule-4.0" type="System.Web.Routing.UrlRoutingModule" />
UrlRoutingModule
并不是MVC特有的,这是一个全局配置,就是说所有的ASP.NET
请求都会到达这里,所以该Module
还不能最终决定是MVC还是WebForm
请求。但是也是至关重要的地方。
(2)通过在全局Web.Config
中注册 System.Web.Routing.UrlRoutingModule
,IIS
请求处理管道接到请求后,就会加载 UrlRoutingModule
类型的Init()
方法。其源码入下:
[TypeForwardedFrom("System.Web.Routing, Version=3.5.0.0, Culture=Neutral, PublicKeyToken=31bf3856ad364e35")]
public class UrlRoutingModule : IHttpModule
{
// Fields
private static readonly object _contextKey = new object();
private static readonly object _requestDataKey = new object();
private RouteCollection _routeCollection;
// Methods
protected virtual void Dispose()
{
}
protected virtual void Init(HttpApplication application)
{
if (application.Context.Items[_contextKey] == null)
{
application.Context.Items[_contextKey] = _contextKey;
application.PostResolveRequestCache += new EventHandler(this.OnApplicationPostResolveRequestCache);
}
}
private void OnApplicationPostResolveRequestCache(object sender, EventArgs e)
{
HttpApplication application = (HttpApplication) sender;
HttpContextBase context = new HttpContextWrapper(application.Context);
this.PostResolveRequestCache(context);
}
[Obsolete("This method is obsolete. Override the Init method to use the PostMapRequestHandler event.")]
public virtual void PostMapRequestHandler(HttpContextBase context)
{
}
public virtual void PostResolveRequestCache(HttpContextBase context)
{
RouteData routeData = this.RouteCollection.GetRouteData(context);
if (routeData != null)
{
IRouteHandler routeHandler = routeData.RouteHandler;
if (routeHandler == null)
{
throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, SR.GetString("UrlRoutingModule_NoRouteHandler"), new object[0]));
}
if (!(routeHandler is StopRoutingHandler))
{
RequestContext requestContext = new RequestContext(context, routeData);
context.Request.RequestContext = requestContext;
IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);
if (httpHandler == null)
{
throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, SR.GetString("UrlRoutingModule_NoHttpHandler"), new object[] { routeHandler.GetType() }));
}
if (httpHandler is UrlAuthFailureHandler)
{
if (!FormsAuthenticationModule.FormsAuthRequired)
{
throw new HttpException(0x191, SR.GetString("Assess_Denied_Description3"));
}
UrlAuthorizationModule.ReportUrlAuthorizationFailure(HttpContext.Current, this);
}
else
{
context.RemapHandler(httpHandler);
}
}
}
}
[TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
void IHttpModule.Dispose()
{
this.Dispose();
}
[TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
void IHttpModule.Init(HttpApplication application)
{
this.Init(application);
}
// Properties
public RouteCollection RouteCollection
{
get
{
if (this._routeCollection == null)
{
this._routeCollection = RouteTable.Routes;
}
return this._routeCollection;
}
[TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
set
{
this._routeCollection = value;
}
}
}
看看上面的UrlRoutingModule
源码里面是怎么实现Init
方法的,Init()
方法里面我标注红色的地方:
application.PostResolveRequestCache += new EventHandler(this.OnApplicationPostResolveRequestCache);
这一步至关重要哈,看到没有,就是对我们在HttpApplication
那19个事件中的PostResolveRequestCache
事件的注册。注册的方法是OnApplicationPostResolveRequestCache
事件。也就是说HttpApplication
对象在执行到PostResolveRequestCache
这个事件的时候,就会执行OnApplicationPostResolveRequestCache
事件。决定是MVC机制处理请求的关键所在就是OnApplicationPostResolveRequestCache
事件。
从源码中我们看出,OnApplicationPostResolveRequestCache
事件执行的时候,最终执行了PostResolveRequestCache
这个方法。最关键的地方呢就在这里了。
当请求到达UrlRoutingModule
的时候,UrlRoutingModule
取出请求中的Controller
、Action
等RouteData
信息,与路由表中的所有规则进行匹配,若匹配,把请求交给IRouteHandler
,即MVCRouteHandler
。我们可以看下UrlRoutingModule
的源码来看看,以下是几句核心的代码:
我们再分析一下这个方法的源码:
public virtual void PostResolveRequestCache(HttpContextBase context)
{
// 通过RouteCollection的静态方法GetRouteData获取到封装路由信息的RouteData实例
RouteData routeData = this.RouteCollection.GetRouteData(context);
if (routeData != null)
{
// 再从RouteData中获取MVCRouteHandler
IRouteHandler routeHandler = routeData.RouteHandler;
......
if (!(routeHandler is StopRoutingHandler))
{
......
// 调用 IRouteHandler.GetHttpHandler(),获取的IHttpHandler 类型实例,它是由 IRouteHandler.GetHttpHandler获取的,这个得去MVC的源码里看
IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);
......
// 合适条件下,把之前将获取的IHttpHandler 类型实例 映射到IIS HTTP处理管道中
context.RemapHandler(httpHandler);
}
}
}
看到了吧,通过路由规则,返回的不为空,说明匹配正确,关于路由规则的匹配,说起来也不短,这里就不大幅介绍,有时间下次再开篇详解路由机制。匹配成功后,返回一个RouteData
类型的对象,RouteData
对象都有些什么属性呢?看看这行源码: IRouteHandler routeHandler = routeData.RouteHandler;
或者看源码我们知道,RouteDate
有一个RouteHandler
属性。
那么UrlRouting Module
是如何选择匹配规则的呢?
我们看看我们新建的MVC应用程序,在App_Start
文件夹下面有一个RouteConfig.cs
类,这个类的内容如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
namespace ApiDemo
{
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
}
我们在这个类里面,主要是给路由表添加路由规则。在看看上面的UrlRoutingModule
类,里面有一个RoutCollection
属性,所以UrlRoutingModule
能够获取路由表中的所有规则,这里值得注意的是,路由规则的匹配是有顺序的,如果有多个规则都能够匹配,UrlRoutingModule
至选择第一个匹配的规则就返回,不再继续往下匹配了。相反的如果一个请求,没有匹配到任何路由,那么该请求就不会被处理。
这里返回的RouteData
里的RouteHandler
就是MVCRouteHandler
。为什么呢?那我们继续往下看RouteHandler
。
RouteHandler
生成MvcHander
在上面路由匹配的过程中,与匹配路由相关联的MvcRouteHandler
,MvcRouteHandler
实现了IRouteHandler
接口。MvcRouteHandler
主要是用来获取对MvcHandler
的引用。MvcHandler
实现了IhttpHandler
接口。
MVCRouteHandler
的作用是用来生成实现IHttpHandler
接口的MvcHandler
。而我们前面说过最终处理请求的都是相对应的HttpHandler
。那么处理MVC请求的自然就是这个MvcHandler
。所以这里返回MvcRouteHandler
至关重要:
那么,MvcRouteHandler
从何而来呢?众所周知,ASP.NET MVC
项目启动是从Global
中的Application_Start()
方法开始的,那就去看看它:
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
}
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
System.Web.Mvc.RouteCollectionExtensions
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
看看我上面标红的代码:这是路由注册,玄机就在这里。那我们去看看MapRoute
源码就知道咯:
public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces)
{
......
Route route = new Route(url, new MvcRouteHandler()) {
Defaults = new RouteValueDictionary(defaults),
Constraints = new RouteValueDictionary(constraints),
DataTokens = new RouteValueDictionary()
};
......
return route;
}
看看我们5-8行代码,在MVC应用程序里,在路由注册的时候,我们就已经给他一个默认的HttpRouteHandler
对象,就是 New MvcRouteHandler()
.现在我们反推回去,我们MVC程序在路由注册的时候就已经确定了HttpRouteHandler
为MvcRouteHandler
,那么当我们在前面PostResolveRequestCache
方法里,当我们的请求与路由匹配成功后,自然会返回的是MvcRouteHandler
。
好啦,MvcRouteHandler
生成了。那么MvcRouteHandler
能做什么呢?又做了什么呢?
再回头看看 PostResolveRequestCache
方法,在成功获取到IHttpRouteHandler
对象即MvcRouteHandler
之后,又做了下面这一个操作:
IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);
我们看看这个IHttpHandler
的源码:
namespace System.Web.Routing
{
public interface IRouteHandler
{
IHttpHandler GetHttpHandler(RequestContext requestContext);
}
}
有一个GetHttpHandler
的方法,恰好就调用了这个方法。那我们看看MvcRouteHandler
是怎么实现这个GetHttpHandler
的呢:
public class MvcRouteHandler : IRouteHandler
{
// Fields
private IControllerFactory _controllerFactory;
// Methods
public MvcRouteHandler()
{
}
public MvcRouteHandler(IControllerFactory controllerFactory)
{
this._controllerFactory = controllerFactory;
}
protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext)
{
requestContext.HttpContext.SetSessionStateBehavior(this.GetSessionStateBehavior(requestContext));
return new MvcHandler(requestContext);
}
protected virtual SessionStateBehavior GetSessionStateBehavior(RequestContext requestContext)
{
string str = (string) requestContext.RouteData.Values["controller"];
if (string.IsNullOrWhiteSpace(str))
{
throw new InvalidOperationException(MvcResources.MvcRouteHandler_RouteValuesHasNoController);
}
IControllerFactory factory = this._controllerFactory ?? ControllerBuilder.Current.GetControllerFactory();
return factory.GetControllerSessionBehavior(requestContext, str);
}
IHttpHandler IRouteHandler.GetHttpHandler(RequestContext requestContext)
{
return this.GetHttpHandler(requestContext);
}
}
看第16-20行代码,这时候应该明白了吧。顺理成章的返回了MvcHandler
对象。记得我们前面说过,请求最终是被相对应的HttpHander
对象处理的。MvcHandler
就是那个用来处理Mvc请求的HttpHandler
。MvcRouteHandler
把请求交给了MvcHandler
去做请求处理管道中后续事件的处理操作了。
下面我们就看看MvcHandler
做了些什么:
MvcHandler
MvcHandler
就是最终对request
进行处理。
MvcHandler
的定义如下:
我们可以看到MvcHandler
就是一个普通的HttpHandler
.我们知道一个httphandler
需要实现一个ProcessRequest()
的方法,这个方法就是处理request
的核心。所以MvcHandler
实现了ProcessRequest()
方法。
ProcessRequest
主要功能:
(1)在ASP.NET MVC中,会调用MvcHandler
的ProcessRequest()
方法,此方法会激活具体请求的Controller
类对象,触发Action
方法,返回ActionResult
实例。
(2)如果ActionResult
是非ViewResult
,比如JsonResult, ContentResult
,这些内容将直接被输送到Response
响应流中,显示给客户端;如果是ViewResult
,就会进入下一个渲染视图环节。
(3)在渲染视图环节,ViewEngine
找到需要被渲染的视图,View
被加载成WebViewPage<TModel>
类型,并渲染生成Html,最终返回Html。
ProcessRequest()
定义如下:
// Copyright (c) Microsoft Open Technologies, Inc.<pre>// All rights reserved. See License.txt in the project root for license information.
void IHttpHandler.ProcessRequest(HttpContext httpContext)
{
ProcessRequest(httpContext);
}
protected virtual void ProcessRequest(HttpContext httpContext)
{
HttpContextBase iHttpContext = new HttpContextWrapper(httpContext);
ProcessRequest(iHttpContext);
}
protected internal virtual void ProcessRequest(HttpContextBase httpContext) {
SecurityUtil.ProcessInApplicationTrust(() => {
IController controller;
IControllerFactory factory;
ProcessRequestInit(httpContext, out controller, out factory);
try
{
controller.Execute(RequestContext);
}
finally
{
factory.ReleaseController(controller);
}
});
}
从上面的代码可以看出调用了一个ProcessRequestInit()
方法,定义如下:
private void ProcessRequestInit(HttpContextBase httpContext,
out IController controller, out IControllerFactory factory) {
// If request validation has already been enabled, make it lazy.
// This allows attributes like [HttpPost] (which looks
// at Request.Form) to work correctly without triggering full validation.
bool? isRequestValidationEnabled =
ValidationUtility.IsValidationEnabled(HttpContext.Current);
if (isRequestValidationEnabled == true) {
ValidationUtility.EnableDynamicValidation(HttpContext.Current);
}
AddVersionHeader(httpContext);
RemoveOptionalRoutingParameters();
// Get the controller type
string controllerName = RequestContext.RouteData.GetRequiredString("controller");
// Instantiate the controller and call Execute
factory = ControllerBuilder.GetControllerFactory();
controller = factory.CreateController(RequestContext, controllerName);
if (controller == null) {
throw new InvalidOperationException(
String.Format(
CultureInfo.CurrentCulture,
MvcResources.ControllerBuilder_FactoryReturnedNull,
factory.GetType(),
controllerName));
}
}
在ProcessRequestInit()
方法中首先创建了ControllerFactory()
的对象 factory.然后ControllerFactory
创建了相关Controller
的实例.最终调用了Controller
的Excute()
方法。
好我们再来看看ControllerFactory
:
ControllerFactory
主要是用来生成Controller
对象
ControllerFactory
实现了接口IControllerFactory
.
Controller
到这里我们大概就知道了,MvcHandler
通过ProcessRequest()
方法最终创建了Controller
对象,这里我们都应该知道,Controller
里面包含很多的Action
方法,每一次请求至少一个Action
方法会被调用。为了明确的实现IController
接口,框架里面有一个ControllerBase
的类已经实现了IController
接口,其实我们自己的Controller
也可以不继承ControllerBase
,只要实现IController
接口即可。
public abstract class ControllerBase : IController
{
protected virtual void Execute(RequestContext requestContext)
{
if (requestContext == null)
{
throw new ArgumentNullException("requestContext");
}
if (requestContext.HttpContext == null)
{
throw new ArgumentException(
MvcResources.ControllerBase_CannotExecuteWithNullHttpContext,
"requestContext");
}
VerifyExecuteCalledOnce();
Initialize(requestContext);
using (ScopeStorage.CreateTransientScope())
{
ExecuteCore();
}
}
protected abstract void ExecuteCore();
// .......
controller
对象实际上使用ActionInvoker
来调用Action
方法的,当Controller
对象被创建后,会执行Controller
对象的基类ControllerBase
类里面的Excute
方法。Excute
方法又调用了ExcuteCore()
方法。Controller
类里面实现了ExcuteCore()
方法。ExcuteCore
调用了ActionInvoker
的InvokerAction
方法来调用Action
方法。
ActionInvoker
ActionInvoker
方法有很重要的责任来查找Controller
中的Action
方法并且调用。
ActionInvoker
是一个实现了IActionInvoker
接口的对象:
bool InvokeAction(
ControllerContext controllerContext,
string actionName
)
Controller
类里面暴露了一个ActionInvoker
属性,会返回一个ControllerActionInvoker
。ActionInvoker
通过CreateActionInvoker()
方法来创建ControllerActionInvoker
对象。
public IActionInvoker ActionInvoker {
get {
if (_actionInvoker == null) {
_actionInvoker = CreateActionInvoker();
}
return _actionInvoker;
}
set {
_actionInvoker = value;
}
}
protected virtual IActionInvoker CreateActionInvoker() {
return new ControllerActionInvoker();
}
我们看到CreateActionInvoker()
是一个Virtual
方法,我们可以实现自己的ActionInvoker
.
ActionInvoker
类需要匹配Controller
中详细的Action来执行,而这些详细的信息是由ControllerDescriptor
提供的。ControllerDescriptor
和ActionDescriptor
在ActionInvoker
中扮演重要的角色。这两个分别是对Controler
和Action
的详细描述。ControllerDescriptor
描述了Controller
的相关信息比如name,action,type
等。
ActionDescriptor
描述了Action
相关的详情,比如name,controller,parameters,attributes
和fiflters
等。
ActionDescriptor
中一个中要的方法就是FindAction()
,这个方法返回一个ActionDescriptor
对象,所以ActionInvoker
知道该调用哪个Action
。
ActionResult
到目前为止,我们看到了Action
方法被ActionInvoker
调用。所有的Action
方法有一个特性,就是返回一个ActionResult
类型的数据。
public abstract class ActionResult
{
public abstract void ExecuteResult(ControllerContext context);
}
ExecuteResult()
是一个抽象方法,所以不同的子类可以提供不同的ExecuteResult()
实现。
ActionResult
执行后响应输出到客户端。
ViewEngine
ViewResult
几乎是大部分应用程序的返回类型,主要通过ViewEngine
引擎来展示view
的。ViewEngine
可能主要就是生成Html元素的引擎。Framwork提供了2种引擎,Razor View Engine
和Web Form View Engine
.如果你想自定义引擎,你可以创建一个引擎只要实现IViewEngine
接口即可。
IViewEngine
有下面几个方法:
1、FindPartialView
:当controller
需要返回一个PartialView
的时候,FindPartialView
方法 就会被调用。
2、FindView
3、ReleaseView
:主要用来有ViewEngine
释放资源
ViewResultBase
和ViewResult
是比较重要的两个类。ViewResultBase
包含下面的实现代码:
if (View == null)
{
result = FindView(context); //calls the ViewResult's FindView() method
View = result.View;
}
ViewContext viewContext = new ViewContext(context, View, ViewData, TempData);
View.Render(viewContext, context.HttpContext.Response.Output);
protected abstract ViewEngineResult FindView(ControllerContext context); //this is implemented by //the ViewResult
protected override ViewEngineResult FindView(ControllerContext context)
{
ViewEngineResult result = ViewEngineCollection.FindView(context, ViewName, MasterName);
if (result.View != null)
{
return result;
}
//rest of the code omitted
}
当ViewResult
的方法ExecuteResult
被调用后,ViewResultBase
的ExecuteResult
方法被调用,然后ViewResultBase
调用ViewResult
的FindView
。紧接着ViewResult
返回ViewEngineResult
,之后ViewEngineResult
调用Render()
方法来绘制html
输出响应。
总结:如果我们理解了整个过程中发生了什么,哪些类和哪些方法被调用,我们就可以在需要扩展的地方轻松的进行扩展。