本文我們會詳細討論MVC應用程式一個要求的生命週期,從一個控制項到另一個控制項是怎麼處理的。我們也會詳細介紹一下整個請求的生命週期中,用到的相關元件。因為在平常的開發過程中,我們可能知道怎麼去使用MVC框架來處理相關的請求,大部分的時候我們只是在controller和action方法之間做相關的處理,對於真正內在的運作機制可能不是很了解。其實當我們對內在機制有了一定的了解以後,會發現微軟的MVC框架的擴展性很強,到處都留有擴展接口,讓我們透過擴展能夠自己定義自己所需要的處理機制,這也正是為什麼MVC框架如此出名的原因。
我們都知道,在ASP.NET MVC框架出現之前,我們大部分開發所使用的框架都是ASP.NET WebForm.其實不管是MVC還是WebForm,在請求處理機制上,大部分是相同的。這牽涉到IIS對請求的處理,涉及的知識較多,我們就不做介紹了,下次有機會我寫一篇專文。我們從HttpApplication說起。先看看微軟官方是怎麼定義HttpApplication的:
微軟官方文件中Remark裡有這麼一段話:HttpApplication 類別的實例是在ASP.NET 基礎結構中創建的,而不是由用戶直接創建的。使用 HttpApplication 類別的一個實例來處理其生存期中收到的眾多請求。但是,它每次只能處理一個請求。這樣,成員變數才可用於儲存針對每個請求的資料。
意思是說ASP.NET應用程序,不管是MVC還是WebForm,最後都會到達一個HttpApplication類別的實例。 HttpApplication是整個ASP.NET基礎架構的核心,負責處理分發給他的請求。 HttpApplication處理請求的週期是一個複雜的過程,在整個過程中,不同階段會觸發相映的事件。我們可以註冊對應的事件,將處理邏輯注入到HttpApplication處理請求的某個階段。在HttpApplication這個類別中定義了19個事件來處理到達HttpApplication實例的請求。是說不管MVC還是WebForm,最終都要經過這19個事件的處理,那麼除了剛才說的MVC和WebFrom在請求處理機制上大部分都是相同的,不同之處在哪呢?他們是從哪裡開始分道揚鑣的呢?我們猜想肯定就在這19個方法中。我們繼續往下看。
事件名稱: |
簡單描述: |
#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 |
|
#PostMapRequestHandler | #在ASP.NET 已將目前請求對應到對應的事件處理程序時發生。 #當ASP.NET 取得與目前請求關聯的目前狀態(如會話狀態)時發生。 ##在已取得與目前請求關聯的請求狀態(例如會話狀態)時發生。 #剛好在ASP.NET 開始執行事件處理程序(例如,某頁或某個XML Web services)前發生。 #在ASP.NET 事件處理程序(例如,某頁或某個XML Web service)執行完畢時發生。 |
在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>
登入後複製
#大家看到沒有,我上面標紅的那一行:
UrlRoutingModule並不是MVC特有的,這是一個全域配置,就是說所有的ASP.NET請求都會到達這裡,ASP.NET請求都會到達這裡,ASP所以該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就是一个普通的Http Handler.我们知道一个http handler需要实现一个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类型,并渲染生成Html,最终返回Html。
ProcessRequest()定义如下:
// Copyright (c) Microsoft Open Technologies, Inc.<pre class="brush:php;toolbar:false">// 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输出响应。
总结:如果我们理解了整个过程中发生了什么,哪些类和哪些方法被调用,我们就可以在需要扩展的地方轻松的进行扩展。
以上这篇基于asp.net MVC 应用程序的生命周期(详解)就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持PHP中文网。
相关推荐:
asp.net mvc中实现Forms身份验证身份验证流程的实例
介绍asp.net MVC下使用rest的方法
ASP.NET MVC 使用Bootstrap方法介绍