Heim  >  Artikel  >  Web-Frontend  >  Lebenszyklus der asp.net MVC-Anwendung (ausführliche Erklärung)_Praktische Tipps

Lebenszyklus der asp.net MVC-Anwendung (ausführliche Erklärung)_Praktische Tipps

韦小宝
韦小宝Original
2017-12-15 14:23:181525Durchsuche

Der unten stehende Herausgeber wird Ihnen eine detaillierte Erklärung des Lebenszyklus basierend auf asp.net MVC-Anwendungen geben. Es hat einen guten Referenzwert und ich hoffe, dass es für alle hilfreich sein wird. Wenn Sie sich für asp.net interessieren, folgen Sie bitte dem Editor, um einen Blick darauf zu werfen.

Zunächst wissen wir, dass http eine zustandslose Anfrage ist. Ihr Lebenszyklus beginnt damit, dass der Client-Browser eine Anfrage sendet Antwort. Fertig. Was macht die MVC-Anwendung also von der Anfrage bis zum Erhalt einer Antwort?

In diesem Artikel besprechen wir ausführlich den Lebenszyklus einer Anfrage in einer MVC-Anwendung und wie sie von einem Steuerelement zum anderen verarbeitet wird. Außerdem werden wir die zugehörigen Komponenten, die im gesamten Anforderungslebenszyklus verwendet werden, im Detail vorstellen. Denn im normalen Entwicklungsprozess wissen wir möglicherweise, wie das MVC-Framework zum Verarbeiten verwandter Anforderungen verwendet wird. Meistens führen wir nur die verwandte Verarbeitung zwischen dem Controller und den Aktionsmethoden durch und wissen möglicherweise nicht viel über den tatsächlichen internen Betriebsmechanismus. Wenn wir ein gewisses Verständnis des internen Mechanismus haben, werden wir feststellen, dass das MVC-Framework von Microsoft sehr erweiterbar ist und überall Erweiterungsschnittstellen hat, sodass wir den Verarbeitungsmechanismus, den wir benötigen, durch Erweiterung definieren können. Aus diesem Grund ist die MVC Rahmen ist so berühmt.

Als ich zum ersten Mal lernte, mvc zu verwenden, beschäftigte mich die Frage: Was ist die Prozesssteuerung einer Anfrage? Was haben Sie von der Ansicht über den Controller bis zur Aktion erlebt? Zu diesem Zeitpunkt wusste ich nicht, welche Rolle das HTTP-Modul und der HTTP-Handler bei der Verarbeitung einer Anfrage spielen. Schließlich ist MVC ein Webentwicklungs-Framework, und der gesamte Anforderungsverarbeitungsprozess muss ein HTTP-Modul und einen HTTP-Handler umfassen. Tatsächlich sind in einem vollständigen MVC-Anwendungsanfragelebenszyklus viele verwandte Komponenten enthalten, und sie alle spielen eine sehr wichtige Rolle im gesamten Anfrageprozess. Obwohl wir die meiste Zeit die vom Framework bereitgestellten Standardfunktionen verwenden, können wir, wenn wir die Rolle jedes Steuerelements verstehen, unsere eigenen Methoden problemlos erweitern und verwenden. Derzeit ist MVC eine Erweiterung eines relativ starken Frameworks.

Das Folgende ist der Hauptinhalt dieses Kapitels:

HttpApplication

HttpModule

HttpHandler

ASP.NET MVC-Laufmechanismus

UrlRoutingModule

RouteHandler

MvcHandler

ControllerFactory

Controller

ActionInvoker

ActionResult

ViewEngine

HttpApplication

Wir alle wissen, dass vor dem Erscheinen des ASP.NET MVC-Frameworks das Framework, das wir für den Großteil unserer Entwicklung verwendet haben, tatsächlich ASP.NET WebForm war, egal ob MVC oder WebForm Die Verarbeitungsmechanismen sind gleich. Dies erfordert die Bearbeitung von Anfragen durch IIS, was viel Wissen erfordert, daher werde ich bei nächster Gelegenheit keinen speziellen Artikel schreiben. Beginnen wir mit HttpApplication. Werfen wir zunächst einen Blick darauf, wie Microsoft HttpApplication offiziell definiert:

Definition von Methoden, Eigenschaften und Ereignissen, die allen Anwendungsobjekten in ASP.NET-Anwendungen gemeinsam sind. Diese Klasse ist die Basisklasse für Anwendungen, die vom Benutzer in der Datei Global.asax definiert werden.

Vielleicht ist meine Übersetzung nicht sehr genau, der Originallink ist hier: https://msdn.microsoft.com/en-us/library/system.web.httpapplication(v= vs. 110).aspx

Es gibt diese Passage in Remark in der offiziellen Dokumentation von Microsoft: Instanzen der HttpApplication-Klasse werden in der ASP.NET-Infrastruktur erstellt, nicht direkt vom Benutzer. Verwenden Sie eine Instanz der HttpApplication-Klasse, um die vielen Anfragen zu verarbeiten, die während ihrer Lebensdauer eingehen. Allerdings kann jeweils nur eine Anfrage bearbeitet werden. Auf diese Weise können Mitgliedsvariablen zum Speichern von Daten für jede Anfrage verwendet werden.

Das bedeutet, dass eine ASP.NET-Anwendung, egal ob MVC oder WebForm, irgendwann eine Instanz der HttpApplication-Klasse erreichen wird. HttpApplication ist der Kern der gesamten ASP.NET-Infrastruktur und für die Verarbeitung der an ihn verteilten Anfragen verantwortlich. Der Anforderungsverarbeitungszyklus von HttpApplication ist ein komplexer Prozess. Während des gesamten Prozesses werden entsprechende Ereignisse in verschiedenen Phasen ausgelöst. Wir können entsprechende Ereignisse registrieren und Verarbeitungslogik in eine bestimmte Phase der HttpApplication-Verarbeitungsanforderungen einfügen. In der HttpApplication-Klasse sind 19 Ereignisse definiert, um Anforderungen zu verarbeiten, die bei der HttpApplication-Instanz eingehen. Das heißt, unabhängig von MVC oder WebForm werden diese 19 Ereignisse letztendlich verarbeitet. Abgesehen von den gerade erwähnten Anforderungsverarbeitungsmechanismen sind MVC und WebFrom also größtenteils gleich. Wo begannen sie, getrennte Wege zu gehen? Wir vermuten, dass es sich um eine dieser 19 Methoden handelt. Schauen wir weiter nach unten.

Werfen wir einen Blick auf diese 19 Ereignisse:

Die Anwendung führt Ereignisse aus, die von Modulen oder Benutzercode behandelt werden, die in der Datei global.asax definiert sind, in der folgenden Reihenfolge:


Tritt ein, wenn das Sicherheitsmodul eine Benutzeridentität eingerichtet hat. Das PostAuthenticateRequest-Ereignis wird ausgelöst, nachdem das AuthenticateRequest-Ereignis auftritt. Funktionen, die das PostAuthenticateRequest-Ereignis abonnieren, können auf alle von PostAuthenticateRequest verarbeiteten Daten zugreifen authentifiziert den Benutzer. Tritt bei Autorisierung auf. Das AuthorizeRequest-Ereignis signalisiert, dass ASP.NET die aktuelle Anfrage autorisiert hat. Durch das Abonnieren des AuthorizeRequest-Ereignisses wird sichergestellt, dass die Anforderung authentifiziert und autorisiert wird, bevor das angehängte Modul oder der Ereignishandler verarbeitet wird PostAuthorizeRequestTritt auf, wenn der aktuell anfordernde Benutzer autorisiert ist. Das PostAuthorizeRequest-Ereignis signalisiert, dass ASP.NET die aktuelle Anfrage autorisiert hat. Durch das Abonnieren des PostAuthorizeRequest-Ereignisses wird sichergestellt, dass die Anforderung authentifiziert und autorisiert wird, bevor das angehängte Modul oder der Handler verarbeitet wird ResolveRequestCacheTritt auf, wenn ASP.NET ein Autorisierungsereignis abschließt, um zu veranlassen, dass das Cache-Modul die Anforderung aus dem Cache bedient, wodurch die Ausführung eines Ereignishandlers (z. B. einer Seite oder eines XML-Webdiensts) übersprungen wird PostResolveRequestCacheTritt auf, wenn ASP.NET die Ausführung des aktuellen Ereignishandlers überspringt und dem Cache-Modul ermöglicht, die Anforderung aus dem Cache zu erfüllen. ) Erstellen Sie einen Ereignishandler (entsprechend der Seite der Anforderungs-URLPostMapRequestHandlerPostReleaseRequestStateUpdateRequestCacheTritt auf, wenn ASP.NET die Ausführung eines Ereignishandlers abgeschlossen hat, sodass das Cache-Modul die Antwort speichert, die zur Bearbeitung nachfolgender Anforderungen aus dem Cache verwendet wird >PostUpdateRequestCache

Ereignisname:

Einfache Beschreibung:

BeginRequest

Tritt als erstes Ereignis in der HTTP-Ausführungspipelinekette auf, wenn ASP.NET auf eine Anfrage antwortet

AuthenticateRequest

Tritt auf, wenn das Sicherheitsmodul eine Benutzeridentität eingerichtet hat. Hinweis: Das AuthenticateRequest-Ereignis signalisiert, dass der konfigurierte Authentifizierungsmechanismus die aktuelle Anfrage authentifiziert hat. Durch das Abonnieren des AuthenticateRequest-Ereignisses wird sichergestellt, dass die Anforderung authentifiziert wird, bevor angehängte Module oder Ereignishandler-Prozeduren verarbeitet werden. >

Tritt ein, wenn ASP.NET die aktuelle Anforderung dem entsprechenden Ereignishandler zugeordnet hat. >Tritt auf, wenn ASP.NET den aktuellen Status (z. B. Sitzungsstatus) erhält, der der aktuellen Anforderung zugeordnet ist (z. B. Sitzungsstatus), der mit der aktuellen Anforderung verknüpft ist, wurde abgerufen, kurz bevor ASP.NET mit der Ausführung eines Ereignishandlers (z. B. einer Seite oder eines XML-Webdienstes) beginnt

PostRequestHandlerExecute

Tritt auf, wenn ein ASP.NET-Ereignishandler (z. B. eine Seite oder ein XML-Webdienst) abgeschlossen wird

ReleaseRequestState

Tritt auf, nachdem ASP.NET alle Anforderungsereignishandler ausgeführt hat 🎜 >

Tritt auf, wenn ASP.NET die Ausführung aller Anforderungsereignishandler abgeschlossen hat und Anforderungsstatusdaten gespeichert wurden 🎜>

Dieses Ereignis tritt auf, nachdem ASP.NET die Aktualisierung des Cache-Moduls abgeschlossen und die Antwort für die Bearbeitung nachfolgender Anforderungen aus dem Cache gespeichert hat.

LogRequest

Schließen Sie die Aktualisierung des Cache-Moduls in ASP.NET ab und speichern Sie es für die spätere Verwendung aus dem Cache Dieses Ereignis tritt auf, nachdem eine Antwort vom Dienst angefordert wurde.

Dieses Ereignis wird nur unterstützt, wenn sich IIS 7.0 im integrierten Modus befindet und das .NET Framework mindestens Version 3.0 ist

PostLogRequest

Tritt auf, nachdem ASP.NET alle Ereignishandler für das LogRequest-Ereignis verarbeitet hat.

Dieses Ereignis wird nur unterstützt, wenn sich IIS 7.0 im integrierten Modus befindet und das .NET Framework mindestens Version 3.0 ist.

EndRequest

tritt als letztes Ereignis in der HTTP-Ausführungspipelinekette auf, wenn ASP.NET auf eine Anfrage antwortet .

Das EndRequest-Ereignis wird immer ausgelöst, wenn die CompleteRequest-Methode aufgerufen wird.

Für eine ASP.NET-Anwendung wird HttpApplication von Global.aspx abgeleitet (Sie können sehen, dass die von uns erstellten Anwendungen eine Global.aspx-Datei haben). Wir können Anforderungen an HttpApplication in der Global.aspx-Datei stellen. Anpassung bedeutet, eine solche einzufügen dieser 19 Ereignisse zur logischen Verarbeitung. In Global.aspx benennen wir das Ereignis entsprechend der Methode „Application_{Event Name}“.

Eventname ist der Name der 19 oben genannten Events. Beispielsweise wird Application_EndRequest verwendet, um das EndRequest-Ereignis von Application zu verarbeiten.

HttpModule

ASP.NET verfügt über eine hoch skalierbare Engine und ist in der Lage, Anfragen für verschiedene Ressourcentypen zu verarbeiten . Dies ist HttpModule. Wenn eine Anforderung an die ASP.net-Pipeline übertragen wird, ist das HttpHandler-Objekt, das der Ressource entspricht, letztendlich für die Verarbeitung der Anforderung verantwortlich. Bevor der HttpHandler jedoch verarbeitet wird, lädt und initialisiert ASP.NET zunächst alle konfigurierten HttpModule-Objekte. Wenn HttpModule initialisiert wird, werden einige Rückrufereignisse in die entsprechenden Ereignisse von HttpApplication eingefügt. Alle HttpModules implementieren die IHttpModule-Schnittstelle, die über eine Init-Methode verfügt.


public interface IHttpModule
{
 // Methods
 void Dispose();
 void Init(HttpApplication context);
}


Achten Sie darauf, dass die Init-Methode ein HttpApplication-Objekt akzeptiert. Mit diesem Objekt ist es einfach, 19 Ereignisse in HttpApplication zu registrieren . eine Veranstaltung in . Auf diese Weise wird das HttpApplication-Objekt, wenn es ein Ereignis ausführt, auf natürliche Weise gestartet.

HttpHandler

Für Anfragen verschiedener Ressourcentypen lädt ASP.NET verschiedene HttpHandler, um sie zu verarbeiten. Alle HttpHandler implementieren die IhttpHandler-Schnittstelle.


public interface IHttpHandler
{
 // Methods
 void ProcessRequest(HttpContext context);

 // Properties
 bool IsReusable { get; }
}


Wir sehen, dass diese Schnittstelle eine Methode ProcessRequest hat, die hauptsächlich zur Verarbeitung von Anfragen verwendet wird . Daher wird jede Anfrage schließlich an den entsprechenden HttpHandler weitergeleitet, um die Anfrage zu bearbeiten.

ASP.NET MVC-Betriebsmechanismus

Okay, so viel wurde oben gesagt, aber es ebnet tatsächlich den Weg dafür. Endlich bin ich zum Punkt gekommen. Schauen Sie sich zunächst das Bild unten an, das die wichtigsten Pipeline-Ereignisse beschreibt, die MVC erlebt:

Das obige Bild zeigt den gesamten Prozess von einer http-Anfrage bis zur Antwort eines vollständigen MVC Der Prozess, den die Gärtnerei durchlaufen hat. Vom UrlRoutingModule, das die Anfrage abfängt, bis zum endgültigen ActionResult, das die ExecuteResult-Methode ausführt, um eine Antwort zu generieren.

Lassen Sie uns im Detail erklären, was diese Prozesse bewirken.

UrlRoutingModule

Der Einstiegspunkt der MVC-Anwendung UrlRoutingModule

Initiieren Sie zunächst eine Anfrage, ASP.NET wird geladen ein HttpModule Das Initialisierungsereignis Init des Objekts und alle HttpModule-Objekte implementieren die IHttpModule-Schnittstelle. Werfen wir einen Blick auf die Implementierung von UrlRoutingModule:

Aus der obigen Abbildung sehen wir, dass UrlRoutingModule die Schnittstelle IHttpModule implementiert, wenn eine Anfrage an die ASP.NET-Pipeline übertragen wird. Die Methode Init() des Objekts wird geladen.

Warum wird UrlRoutingModule geladen und initialisiert? Warum nicht ein weiteres HttpModule-Objekt? Mit dieser Frage im Hinterkopf machen wir weiter.

In ASP.NET MVC ist der Kern zweifellos das „Routing-System“, und der Kern des Routing-Systems kommt von einer leistungsstarken System.Web.Routing.dll Komponente. System.Web.Routing.dll gibt es nicht nur bei MVC, aber das MVC-Framework ist untrennbar damit verbunden.

Zuerst müssen wir verstehen, wie UrlRoutingModule funktioniert.

(1) Die Konfiguration der IIS-Website kann in zwei Blöcke unterteilt werden: globale Web.config und lokale Web.config. Asp.Net Routing ist global und wird daher in der globalen Web.Config konfiguriert. Wir finden es im folgenden Pfad: „CWindowsMicrosoft.NETFramework Versionsnummer 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>


Haben Sie die Zeile gesehen, die ich oben rot markiert habe: b525daaaed216055d87f0223b4905bde

UrlRoutingModule ist nicht nur für MVC gültig. Dies ist eine globale Konfiguration, was bedeutet, dass alle ASP.NET-Anfragen hier ankommen, also die endgültige Entscheidung darüber Das Modul kann noch nicht per MVC oder WebForm angefordert werden. Aber es ist auch ein entscheidender Ort.

(2) Durch die Registrierung von System.Web.Routing.UrlRoutingModule in der globalen Web.Config lädt die IIS-Anforderungsverarbeitungspipeline die Init von, nachdem sie die Anforderung empfangen hat UrlRoutingModule-Typ ()Methode. Der Quellcode lautet wie folgt:


[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;
 }
 }
}


Sehen Sie sich an, wie die Init-Methode und die Init()-Methode im UrlRoutingModule implementiert sind Quellcode oben. Darin habe ich die Stelle rot markiert:

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被加载成WebViewPagebc26e2d35896972142bded266172216c类型,并渲染生成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&#39;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方法介绍

Das obige ist der detaillierte Inhalt vonLebenszyklus der asp.net MVC-Anwendung (ausführliche Erklärung)_Praktische Tipps. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn