First of all, we know that http is a stateless request. Its life cycle starts from the client browser sending a request to getting a response. Finish. So what does the MVC application do from making a request to getting a response?
In this article we will discuss in detail the life cycle of a request in an MVC application and how it is processed from one control to another. We will also introduce in detail the relevant components used in the entire request life cycle. Because in the normal development process, we may know how to use the MVC framework to process related requests. Most of the time we only do related processing between the controller and action methods, and may not know much about the real internal operating mechanism. . In fact, when we have a certain understanding of the internal mechanism, we will find that Microsoft's MVC framework is very extensible, with extension interfaces everywhere, allowing us to define the processing mechanism we need through extension. This is why The reason why the MVC framework is so famous.
When I first learned to use mvc, one of the questions that bothered me was, what is the process control of a request? What did you experience from view to controller to action? At that time, I didn't know what role the HTTP module and HTTP handler played in processing a request. After all, MVC is a web development framework, and the entire request processing process must include http module and http handler. In fact, there are many related components included in a complete MVC application request life cycle, and they all play a very important role in the entire request process. Although most of the time we use the default functions provided by the framework, if we understand the role of each control, we can easily extend and use our own methods. For now, MVC is an extension A relatively strong framework.
We all know that before the ASP.NET MVC framework appeared, the framework we used for most of our development was ASP.NET WebForm. In fact, whether it is MVC or WebForm, most of the request processing mechanisms are identical. This involves the processing of requests by IIS, which involves a lot of knowledge, so we will not introduce it. I will write a special article next time I have the opportunity. Let's start with HttpApplication. Let’s first take a look at how Microsoft officially defines HttpApplication:
Define the methods, properties, and events common to all application objects in the ASP.NET application. This class is the base class for applications defined by the user in the Global.asax file.
There is a passage in Remark in Microsoft's official documentation: Instances of the HttpApplication class are created in the ASP.NET infrastructure, not directly by the user. Use an instance of the HttpApplication class to handle the many requests received during its lifetime. However, it can only handle one request at a time. This way, member variables can be used to store data for each request.
This means that an ASP.NET application, whether it is MVC or WebForm, will eventually reach an instance of the HttpApplication class. HttpApplication is the core of the entire ASP.NET infrastructure and is responsible for processing requests distributed to him. The request processing cycle of HttpApplication is a complex process. During the entire process, corresponding events will be triggered at different stages. We can register corresponding events and inject processing logic into a certain stage of HttpApplication processing requests. There are 19 events defined in the HttpApplication class to handle requests arriving at the HttpApplication instance. That is to say, regardless of MVC or WebForm, these 19 events will eventually be processed. So except for the request processing mechanisms just mentioned, MVC and WebFrom are mostly the same. What are the differences? Where did they start to go their separate ways? We guess it must be among these 19 methods. Let's continue reading.
The application executes events handled by modules or user code defined in the global.asax file in the following order:
Event name: |
##Simple description: |
BeginRequest
|
Occurs as the first event in the HTTP execution pipeline chain when ASP.NET responds to a request
|
AuthenticateRequest
|
Occurs when the security module has established a user identity. Note: The AuthenticateRequest event signals that the configured authentication mechanism has authenticated the current request. Subscribing to the AuthenticateRequest event ensures that the request is authenticated before processing attached modules or event handlers
|
PostAuthenticateRequest
|
Occurs when the security module has established a user identity. The PostAuthenticateRequest event is raised after the AuthenticateRequest event occurs. Functions that subscribe to the PostAuthenticateRequest event can access any data handled by PostAuthenticateRequest
|
AuthorizeRequest
|
When the security module has authenticated the user Occurs when authorized. The AuthorizeRequest event signals that ASP.NET has authorized the current request. Subscribing to the AuthorizeRequest event ensures that the request is authenticated and authorized before the attached module or event handler is processed
|
PostAuthorizeRequest
| Occurs when the currently requesting user is authorized. The PostAuthorizeRequest event signals that ASP.NET has authorized the current request. Subscribing to the PostAuthorizeRequest event ensures that the request is authenticated and authorized before the attached module or handler is processed
|
ResolveRequestCache
|
Occurs when ASP.NET completes an authorization event to cause the cache module to serve a request from the cache, thereby skipping the execution of an event handler (such as a page or XML Web service)
|
PostResolveRequestCache
|
Occurs when ASP.NET skips execution of the current event handler and allows the cache module to satisfy the request from the cache. ) after the PostResolveRequestCache event and before the PostMapRequestHandler event (corresponding to the page requesting the URL
|
##PostMapRequestHandler | Occurs when ASP.NET has mapped the current request to the appropriate event handler |
#AcquireRequestState
Occurs when ASP.NET obtains the current state (such as session state) associated with the current request |
| #PostAcquireRequestState
Occurs when the request state (such as session state) associated with the current request has been obtained |
#PreRequestHandlerExecute |
Occurs just before ASP.NET starts executing an event handler (for example, a page or an XML Web service)
|
##PostRequestHandlerExecute |
Occurs when an ASP.NET event handler (for example, a page or an XML Web service) completes execution |
#ReleaseRequestState.
| ##Occurs after ASP.NET has executed all request event handlers.
This event will cause the state module to save the current state data. ##PostReleaseRequestState |
Occurs when ASP.NET has completed executing all request event handlers and the request state data has been stored |
#. ##UpdateRequestCache |
Occurs when ASP.NET has finished executing an event handler to enable the cache module to store the response that will be used to serve subsequent requests from the cache. |
PostUpdateRequestCache |
This event occurs after ASP.NET has completed updating the cache module and stored the response for serving subsequent requests from the cache. |
LogRequest |
Complete the update of the cache module in ASP.NET and store it for subsequent use from the cache This event occurs after requesting a response from the service.
This event is only supported if IIS 7.0 is in integrated mode and the .NET Framework is at least version 3.0
|
##PostLogRequest
|
Occurs after ASP.NET has processed all event handlers for the LogRequest event. This event is only supported if IIS 7.0 is in integrated mode and the .NET Framework is at least version 3.0.
|
EndRequest
|
Occurs as the last event in the HTTP execution pipeline chain when ASP.NET responds to a request. The EndRequest event is always raised when the CompleteRequest method is called.
|
For an ASP.NET application, HttpApplication is derived from Global.aspx (you can see that the applications we created have a Global.aspx file). We can make requests to HttpApplication in the Global.aspx file. Customization means injecting one of these 19 events for logical processing. In Global.aspx, we register events according to the method naming "Application_{Event Name}".
Event Name is the name of the above 19 events. For example, Application_EndRequest is used to handle the EndRequest event of Application.
HttpModule
ASP.NET has a highly extensible engine and can handle different resource types. ask. This is HttpModule. When a request is transferred to the ASP.net pipeline, the HttpHandler object that matches the resource is ultimately responsible for processing the request, but before the HttpHandler is processed, ASP.NET will first load and initialize all configured HttpModule objects. When HttpModule is initialized, some callback events will be injected into the corresponding events of HttpApplication. All HttpModules implement the IHttpModule interface, which has an Init method.
public interface IHttpModule
{
// Methods
void Dispose();
void Init(HttpApplication context);
}
Copy after login
See that the Init method accepts an HttpApplication object. With this object, it is easy to register one of the 19 events in HttpApplication. Some event. In this way, when the HttpApplication object executes an event, it will naturally start.
HttpHandler
For requests of different resource types, ASP.NET will load different HttpHandlers to handle them. All HttpHandlers implement the IhttpHandler interface.
public interface IHttpHandler
{
// Methods
void ProcessRequest(HttpContext context);
// Properties
bool IsReusable { get; }
}
Copy after login
We see that this interface has a method ProcessRequest. As the name suggests, this method is mainly used to process requests. So each request is eventually distributed to its corresponding HttpHandler to handle the request.
ASP.NET MVC operating mechanism
Okay, so much has been said above, but it is actually paving the way for here. Finally got to the point. First look at the picture below, which describes the main pipeline events experienced by MVC:
The above picture is the entire process from an http request to the response of a complete mvc application. The process the nursery went through. From the UrlRoutingModule intercepting the request to the final ActionResult executing the ExecuteResult method to generate a response.
Let’s explain in detail what these processes do.
UrlRoutingModule
The entry point of MVC application UrlRoutingModule
First initiate a request. As we mentioned earlier, ASP.NET will load an HttpModule. The object's initialization event Init, and all HttpModule objects implement the IHttpModule interface. Let’s take a look at the implementation of UrlRoutingModule:
## From the above figure we see that UrlRoutingModule implements the interface IHttpModule. When a request is transferred to the ASP.NET pipeline, UrlRoutingModule will be loaded. The Init() method of the object.
So why is UrlRoutingModule loaded and initialized? Why not another HttpModule object? With this question in mind we continue. In ASP.NET MVC, the core is undoubtedly the "routing system", and the core of the routing system comes from a powerful System.Web.Routing.dll Components. System.Web.Routing.dll is not unique to MVC, but the MVC framework is inseparable from it.
First, we need to understand how UrlRoutingModule works. (1) The configuration of the IIS website can be divided into two blocks: global Web.config and local Web.config. Asp.Net Routing is global, so it is configured in the global Web.Config. We can find it in the following path: "C\Windows\Microsoft.NET\Framework\version number\Config\Web.config", I extracted Let’s take a look at some important configurations:
<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>
Copy after login
Do you see the line I marked in red above: UrlRoutingModule is not unique to MVC. This is a global configuration, which means that all ASP.NET requests will arrive here. So the Module cannot make the final decision whether it is an MVC or WebForm request. But it is also a crucial place. (2) By registering System.Web.Routing.UrlRoutingModule in the global Web.Config, after the IIS request processing pipeline receives the request, it will load the Init of the UrlRoutingModule type ()method. The source code is as follows:
[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;
}
}
}
Copy after login
Look at how the Init method is implemented in the UrlRoutingModule source code above. In the Init() method, I Marked in red: 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);
}
}
}
Copy after login
看到了吧,通过路由规则,返回的不为空,说明匹配正确,关于路由规则的匹配,说起来也不短,这里就不大幅介绍,有时间下次再开篇详解路由机制。匹配成功后,返回一个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 }
);
}
}
}
Copy after login
我们在这个类里面,主要是给路由表添加路由规则。在看看上面的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 }
);
}
}
Copy after login
看看我上面标红的代码:这是路由注册,玄机就在这里。那我们去看看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;
}
Copy after login
看看我们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);
}
}
Copy after login
有一个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);
}
}
Copy after login
看第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);
}
});
}
Copy after login
从上面的代码可以看出调用了一个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));
}
}
Copy after login
在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();
// .......
Copy after login
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
)
Copy after login
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();
}
Copy after login
我们看到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);
}
Copy after login
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
Copy after login
protected override ViewEngineResult FindView(ControllerContext context)
{
ViewEngineResult result = ViewEngineCollection.FindView(context, ViewName, MasterName);
if (result.View != null)
{
return result;
}
//rest of the code omitted
}
Copy after login
当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方法介绍
The above is the detailed content of Life cycle of asp.net MVC application (detailed explanation)_Practical tips. For more information, please follow other related articles on the PHP Chinese website!