L'éditeur ci-dessous partagera avec vous une explication détaillée du cycle de vie basé sur les applications asp.net MVC. Il a une bonne valeur de référence et j'espère qu'il sera utile à tout le monde. Si vous êtes intéressé par asp.net, veuillez suivre l'éditeur pour y jeter un œil
Tout d'abord, nous savons que http est une requête sans état. Son cycle de vie commence depuis l'envoi d'une requête par le navigateur client jusqu'à l'obtention d'une requête. réponse. Alors, que fait l'application MVC, depuis la demande jusqu'à l'obtention d'une réponse ?
Dans cet article, nous aborderons en détail le cycle de vie d'une requête dans une application MVC et comment elle est traitée d'un contrôle à l'autre. Nous présenterons également en détail les composants pertinents utilisés dans tout le cycle de vie de la demande. Parce que dans le processus de développement normal, nous pouvons savoir comment utiliser le cadre MVC pour traiter les demandes associées. La plupart du temps, nous effectuons uniquement des traitements liés entre le contrôleur et les méthodes d'action, et nous ne savons peut-être pas grand-chose sur le véritable mécanisme de fonctionnement interne. En fait, lorsque nous aurons une certaine compréhension du mécanisme interne, nous constaterons que le framework MVC de Microsoft est très extensible, avec des interfaces d'extension partout, nous permettant de définir le mécanisme de traitement dont nous avons besoin via l'extension. C'est pourquoi le MVC. le cadre est si célèbre.
Quand j'ai appris à utiliser mvc pour la première fois, une question qui m'a troublé était : quel est le contrôle de processus d'une requête ? Qu’avez-vous vécu de la vue au contrôleur jusqu’à l’action ? À cette époque, je ne savais pas quel rôle le module HTTP et le gestionnaire HTTP jouaient dans le traitement d'une requête. Après tout, MVC est un framework de développement Web et l'ensemble du processus de traitement des requêtes doit inclure le module http et le gestionnaire http. En fait, de nombreux composants associés sont inclus dans le cycle de vie complet d’une demande d’application MVC, et ils jouent tous un rôle très important dans l’ensemble du processus de demande. Bien que la plupart du temps nous utilisons les fonctions par défaut fournies par le framework, si nous comprenons le rôle de chaque contrôle, nous pouvons facilement étendre et utiliser nos propres méthodes. Pour l'instant, MVC est une extension d'un framework relativement solide.
Ce qui suit est le contenu principal de ce chapitre :
HttpApplication
HttpModule
HttpHandler
Mécanisme d'exécution ASP.NET MVC
UrlRoutingModule
RouteHandler
MvcHandler
ControllerFactory
Contrôleur
ActionInvoker
ActionResult
ViewEngine
HttpApplication
HttpApplication
Nous savons tous qu'avant l'apparition du framework ASP.NET MVC, le framework que nous utilisions pour la plupart de notre développement était ASP.NET WebForm. En fait, qu'il s'agisse de MVC ou de WebForm, la plupart des requêtes. les mécanismes de traitement sont les mêmes. Cela implique le traitement des requêtes par IIS, qui implique beaucoup de connaissances, nous ne le présenterons donc pas j'écrirai un article spécial la prochaine fois que j'en aurai l'occasion. Commençons par HttpApplication. Voyons d'abord comment Microsoft définit officiellement HttpApplication : Définition des méthodes, propriétés et événements communs à tous les objets d'application dans les applications ASP.NET. Cette classe est la classe de base des applications définies par l'utilisateur dans le fichier Global.asax.
Peut-être que ma traduction n'est pas très précise, le lien original est ici :
https://msdn.microsoft.com/en-us/library/system.web.httpapplication(v= vs. 110).aspx
Il y a ce passage dans Remark dans la documentation officielle de Microsoft : Les instances de la classe HttpApplication sont créées dans l'infrastructure ASP.NET, et non directement par l'utilisateur. Utilisez une instance de la classe HttpApplication pour gérer les nombreuses requêtes reçues au cours de sa durée de vie. Cependant, il ne peut traiter qu’une seule demande à la fois. De cette façon, les variables membres peuvent être utilisées pour stocker les données pour chaque requête. Cela signifie qu'une application ASP.NET, qu'elle soit MVC ou WebForm, finira par atteindre une instance de la classe HttpApplication. HttpApplication est le cœur de toute l'infrastructure ASP.NET et est responsable du traitement des requêtes qui lui sont distribuées. Le cycle de traitement des requêtes de HttpApplication est un processus complexe. Pendant tout le processus, les événements correspondants seront déclenchés à différentes étapes. Nous pouvons enregistrer les événements correspondants et injecter une logique de traitement dans une certaine étape du traitement des requêtes HttpApplication. Il existe 19 événements définis dans la classe HttpApplication pour gérer les requêtes arrivant à l'instance HttpApplication. Autrement dit, quels que soient MVC ou WebForm, ces 19 événements seront finalement traités. Ainsi, à l'exception des mécanismes de traitement des requêtes que nous venons de mentionner, MVC et WebFrom sont pour la plupart identiques. Où ont-ils commencé à se séparer ? Nous pensons que cela doit faire partie de ces 19 méthodes. Continuons à regarder vers le bas.Jetons un coup d'œil à ces 19 événements :
L'application exécute les événements gérés par les modules ou le code utilisateur défini dans le fichier global.asax dans l'ordre suivant :
Nom de l'événement : |
Description simple : |
BeginRequest |
Se produit comme premier événement dans la chaîne de pipeline d'exécution HTTP lorsqu'ASP.NET répond à une requête |
AuthenticateRequest |
Se produit lorsque le module de sécurité a établi une identité d'utilisateur. Remarque : L'événement AuthenticateRequest signale que le mécanisme d'authentification configuré a authentifié la demande en cours. L'abonnement à l'événement AuthenticateRequest garantit que la demande est authentifiée avant de traiter les modules attachés ou les gestionnaires d'événements procédures > | Se produit lorsque le module de sécurité a établi une identité d'utilisateur. L'événement PostAuthenticateRequest est déclenché après l'événement AuthenticateRequest. Les fonctions qui s'abonnent à l'événement PostAuthenticateRequest peuvent accéder à toutes les données gérées par PostAuthenticateRequest
AuthorizeRequest | lorsque le module de sécurité a authentifié l'utilisateur Se produit lorsqu'il est autorisé. L'événement AuthorizeRequest signale qu'ASP.NET a autorisé la demande en cours. L'abonnement à l'événement AuthorizeRequest garantit que la demande est authentifiée et autorisée avant que le module ou le gestionnaire d'événements attaché ne soit traité |
PostAuthorizeRequest | Se produit lorsque l'utilisateur actuellement demandeur est autorisé. L'événement PostAuthorizeRequest signale qu'ASP.NET a autorisé la demande en cours. L'abonnement à l'événement PostAuthorizeRequest garantit que la demande est authentifiée et autorisée avant que le module ou le gestionnaire attaché ne soit traité |
ResolveRequestCache | Se produit lorsque ASP.NET termine un événement d'autorisation pour que le module de cache traite la requête à partir du cache, ignorant ainsi l'exécution d'un gestionnaire d'événements (tel qu'une page ou un service Web XML) |
PostResolveRequestCache | Se produit lorsque ASP.NET ignore l'exécution du gestionnaire d'événements actuel et permet au module de cache de satisfaire la demande du cache. ) créer un gestionnaire d'événements (correspondant à la page de l'URL de la requête |
PostMapRequestHandler | Se produit lorsque ASP.NET a mappé la requête actuelle au gestionnaire d'événements correspondant. > Se produit lorsque ASP.NET obtient l'état actuel (tel que l'état de session) associé à la requête actuelle. 🎜> Se produit lorsque l'état de la requête (tel que l'état de session) est associé à la requête actuelle. La requête a été obtenue. > Se produit juste avant qu'ASP.NET ne commence à exécuter un gestionnaire d'événements (par exemple, une page ou un service Web XML) |
PostRequestHandlerExecute | Se produit lorsqu'un gestionnaire d'événements ASP.NET (par exemple, une page ou un service Web XML) termine |
ReleaseRequestState . | Se produit après qu'ASP.NET a exécuté tous les gestionnaires d'événements de requête |
Se produit lorsque ASP.NET a terminé l'exécution de tous les gestionnaires d'événements de demande et que les données d'état de la demande ont été stockées 🎜>UpdateRequestCache | Se produit lorsque. ASP.NET a terminé l'exécution d'un gestionnaire d'événements afin que le module de cache stocke la réponse qui sera utilisée pour répondre aux requêtes ultérieures du cache |
PostUpdateRequestCache | Cet événement se produit une fois qu'ASP.NET a terminé la mise à jour du module de cache et stocké la réponse pour répondre aux requêtes suivantes à partir du cache. |
LogRequest |
Terminez la mise à jour du module de cache dans ASP.NET et stockez-le pour une utilisation ultérieure à partir du cache Cet événement se produit après avoir demandé une réponse au service. Cet événement n'est pris en charge que si IIS 7.0 est en mode intégré et que le .NET Framework est au moins en version 3.0 |
PostLogRequest |
Se produit après qu'ASP.NET a traité tous les gestionnaires d'événements pour l'événement LogRequest. Cet événement n'est pris en charge que si IIS 7.0 est en mode intégré et que le .NET Framework est au moins en version 3.0. |
EndRequest |
se produit comme dernier événement de la chaîne de pipeline d'exécution HTTP lorsqu'ASP.NET répond à une requête . L'événement EndRequest est toujours déclenché lorsque la méthode CompleteRequest est appelée. |
Pour une application ASP.NET, HttpApplication est dérivé de Global.aspx (vous pouvez voir que les applications que nous avons créées ont un fichier Global.aspx), nous pouvons faire des requêtes à HttpApplication dans le fichier Global.aspx. La personnalisation consiste à en injecter un de ces 19 événements pour le traitement logique. Dans Global.aspx, nous enregistrons les événements selon la méthode nommant « Application_{Event Name} ».
Le nom de l'événement est le nom des 19 événements ci-dessus. Par exemple, Application_EndRequest est utilisé pour gérer l'événement EndRequest d'Application.
HttpModule
ASP.NET dispose d'un moteur hautement évolutif et est capable de gérer les requêtes pour différents types de ressources. . Il s'agit de HttpModule. Lorsqu'une requête est transférée vers le pipeline ASP.net, l'objet HttpHandler qui correspond à la ressource est en dernier ressort responsable du traitement de la requête, mais avant que le HttpHandler ne soit traité, ASP.NET chargera et initialisera d'abord tous les objets HttpModule configurés. Lorsque HttpModule est initialisé, certains événements de rappel seront injectés dans les événements correspondants de HttpApplication. Tous les HttpModules implémentent l'interface IHttpModule, qui possède une méthode Init.
public interface IHttpModule { // Methods void Dispose(); void Init(HttpApplication context); }
Voyez que la méthode Init accepte un objet HttpApplication Avec cet objet, il est facile d'enregistrer 19 événements dans HttpApplication. .un événement en . De cette façon, lorsque l'objet HttpApplication exécute un événement, celui-ci démarre naturellement.
HttpHandler
Pour les requêtes de différents types de ressources, ASP.NET chargera différents HttpHandlers pour les gérer. Tous les HttpHandlers implémentent l'interface IhttpHandler.
public interface IHttpHandler { // Methods void ProcessRequest(HttpContext context); // Properties bool IsReusable { get; } }
On voit que cette interface possède une méthode ProcessRequest Comme son nom l'indique, cette méthode est principalement utilisée pour traiter les requêtes. . Ainsi, chaque requête est finalement distribuée à son HttpHandler correspondant pour gérer la requête.
Mécanisme d'exploitation ASP.NET MVC
D'accord, beaucoup de choses ont été dites ci-dessus, mais cela ouvre en fait la voie à cela. Enfin arrivé au point. Regardez d'abord l'image ci-dessous, qui décrit les principaux événements de pipeline rencontrés par MVC :
L'image ci-dessus représente l'ensemble du processus depuis une requête http jusqu'à la réponse d'un MVC complet. demande. Le processus suivi par la crèche. Du UrlRoutingModule interceptant la requête au ActionResult final exécutant la méthode ExecuteResult pour générer une réponse.
Expliquons en détail ce que font ces processus.
UrlRoutingModule
Le point d'entrée de l'application MVC UrlRoutingModule
Lancez d'abord une requête Comme nous l'avons mentionné précédemment, ASP.NET se chargera. un HttpModule L'événement d'initialisation de l'objet Init et tous les objets HttpModule implémentent l'interface IHttpModule. Jetons un coup d'œil à l'implémentation de UrlRoutingModule :
D'après la figure ci-dessus, nous voyons que UrlRoutingModule implémente l'interface IHttpModule Lorsqu'une requête est transférée vers le pipeline ASP.NET, UrlRoutingModule sera chargé. La méthode Init() de l'objet.
Alors pourquoi UrlRoutingModule est-il chargé et initialisé ? Pourquoi pas un autre objet HttpModule ? C’est avec cette question à l’esprit que nous poursuivons.
Dans ASP.NET MVC, le cœur est sans aucun doute le "système de routage", et le cœur du système de routage provient d'un puissant System.Web.Routing.dll Composant. System.Web.Routing.dll n'est pas unique à MVC, mais le framework MVC en est indissociable.
Tout d’abord, nous devons comprendre comment fonctionne UrlRoutingModule.
(1) La configuration du site Web IIS peut être divisée en deux blocs : Web.config global et Web.config local. Le routage Asp.Net est global, il est donc configuré dans le Web.Config global. Nous pouvons le trouver dans le chemin suivant : "Numéro de version CWindowsMicrosoft.NETFramework ConfigWeb.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>
Avez-vous vu la ligne que j'ai marquée en rouge ci-dessus :
UrlRoutingModule n'est pas unique à MVC. Il s'agit d'une configuration globale, ce qui signifie que toutes les requêtes ASP.NET arriveront ici, donc la décision finale à ce sujet. Le module ne peut pas encore être effectué. Demande MVC ou WebForm. Mais c'est aussi un lieu crucial.
(2) En enregistrant System.Web.Routing.UrlRoutingModule dans le Web.Config global, une fois que le pipeline de traitement des requêtes IIS aura reçu la requête, il chargera le Init de Type UrlRoutingModule ()méthode. Le code source est le suivant :
[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; } } }
Regardez comment les méthodes Init et Init() sont implémentées dans UrlRoutingModule code source ci-dessus. À l'intérieur, j'ai marqué l'endroit en rouge :
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
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身份验证身份验证流程的实例
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!