asp.net MVCアプリケーションのライフサイクル(詳細解説)_実践的なヒント

韦小宝
リリース: 2017-12-15 14:23:18
オリジナル
1482 人が閲覧しました

以下のエディターは、asp.net MVC アプリケーションに基づいた ライフ サイクル の詳細な説明を共有します。これは優れた参考値であり、皆様のお役に立てれば幸いです。 asp.net に興味がある場合は、エディターをフォローしてご覧ください

まず、http はステートレス リクエストであることがわかります。そのライフ サイクルは、クライアント ブラウザーがリクエストを送信することから始まり、レスポンスで終わります。では、MVC アプリケーションはリクエストを送信してから応答を取得するまで何を行うのでしょうか?

この記事では、MVC アプリケーションにおけるリクエストのライフサイクルと、リクエストが 1 つのコントロールから別のコントロールにどのように処理されるかについて詳しく説明します。また、リクエストのライフサイクル全体で使用される関連コンポーネントについても詳しく紹介します。なぜなら、通常の開発プロセスでは、MVC フレームワークを使用して関連するリクエストを処理する方法を知っているだけで、実際の内部動作メカニズムについてはほとんど知らないからです。実際、内部メカニズムをある程度理解すると、Microsoft の MVC フレームワークは非常に拡張性があり、随所に拡張インターフェイスがあり、拡張を通じて必要な処理メカニズムを定義できることがわかります。これが MVC の理由です。フレームワークはとても有名です。

私が初めて mvc の使い方を学んだとき、私を悩ませた質問は、リクエストの プロセス制御 とは何ですか?ということでした。ビューからコントローラー、アクションまで何を体験しましたか?当時、私は HTTP モジュールと HTTP ハンドラーがリクエストの処理においてどのような役割を果たしているのか知りませんでした。結局のところ、MVC は Web 開発フレームワークであり、リクエスト処理プロセス全体に http モジュールと http ハンドラーが含まれている必要があります。実際、完全な MVC アプリケーション リクエスト ライフ サイクルには多くの関連コンポーネントが含まれており、それらはすべてリクエスト プロセス全体において非常に重要な役割を果たします。ほとんどの場合、フレームワークが提供するデフォルトの機能を使用しますが、各コントロールの役割を理解していれば、現時点では、MVC は拡張機能としては比較的強力なフレームワークです。

この章の主な内容は次のとおりです:

HttpApplication

HttpModule

HttpHandler

ASP.NET MVCの動作メカニズム

URLRoutingModule

RouteHandler

MvcHandler

ControllerFactory

Controller

ActionInvoker

ActionResult

ViewEngine

HttpApplication

ASP.NET MVC フレームワークが登場する前から、それは誰もが知っています。ほとんどの開発で使用されるフレームワークは ASP.NET WebForm です。実際、MVC であっても WebForm であっても、リクエスト処理メカニズムのほとんどは同じです。 IIS によるリクエストの処理には多くの知識が必要となるため、次回機会があれば特別に記事を書きます。 HttpApplication から始めましょう。まず、Microsoft が HttpApplication をどのように公式に定義しているかを見てみましょう。

ASP.NET アプリケーションのすべてのアプリケーション オブジェクトに共通のメソッド、プロパティ、およびイベントを定義します。このクラスは、ユーザーが Global.asax ファイルで定義したアプリケーションの基本クラスです。

私の翻訳はあまり正確ではないかもしれません。元のリンクはここにあります: https://msdn.microsoft.com/en-us/library/system.web.httpapplication(v=vs.110).aspx

Microsoft 公式 ドキュメントの注釈に次のような一節があります。 HttpApplication クラスのインスタンスは、ユーザーによって直接作成されるのではなく、ASP.NET インフラストラクチャ内に作成されます。 HttpApplication クラスのインスタンスを使用して、その存続期間中に受信した多くの要求を処理します。ただし、一度に処理できるリクエストは 1 つだけです。このようにして、メンバー変数を使用して各リクエストのデータを保存できます。

これは、ASP.NET アプリケーションが、MVC であっても WebForm であっても、最終的には HttpApplication クラスのインスタンスに到達することを意味します。 HttpApplication は ASP.NET インフラストラクチャ全体の中核であり、配布された要求の処理を担当します。 HttpApplication のリクエスト処理サイクルは複雑なプロセスです。プロセス全体を通じて、対応するイベントがさまざまな段階でトリガーされます。対応するイベントを登録し、HttpApplication 処理リクエストの特定の段階に処理ロジックを挿入できます。 HttpApplication インスタンスに到着するリクエストを処理するために、HttpApplication クラスには 19 のイベントが定義されています。つまり、MVC か WebForm に関係なく、これら 19 個のイベントは最終的に処理されます。では、先ほど述べたリクエスト処理メカニズムを除けば、MVC と WebFrom はほとんど同じです。彼らはどこから別々の道を歩み始めたのでしょうか?この 19 の方法のいずれかに該当するはずです。読み続けてみましょう。

これら 19 個のイベントを見てみましょう:

アプリケーションは、global.asax ファイルで定義されたモジュールまたはユーザー コードによって処理されるイベントを次の順序で実行します。


は、現在のイベント ハンドラーの実行をスキップし、キャッシュ モジュールがからの要求を満たす キャッシュが要求されたときに発生します。 ) は、PostResolveRequestCache イベントの後、ASP.NET が現在の要求を対応するイベント ハンドラーにマップしたときに PostMapRequestHandler が発生する前に、イベント ハンドラー (要求 URL に対応するページ) を作成します。 ASP.NET が現在の要求に関連付けられた現在の状態 (セッション状態など) を取得するときに発生します。 ASP.NET がすべての要求イベント ハンドラーの実行を完了し、要求状態データが保存されたときに発生します。ハンドラーを使用して、キャッシュ モジュールがキャッシュからの後続のリクエストを処理するために使用される応答を保存するようにします

イベント名:

簡単な説明:

BeginRequest

in A SP.NET 応答時に HTTP 実行パイプライン チェーンの最初として機能します。リクエストに対して イベントが発生します

AuthenticateRequest

セキュリティ モジュールがユーザー ID を確立したときに発生します。注: AuthenticateRequest イベントは、構成された認証メカニズムが現在のリクエストを認証したことを通知します。 AuthenticateRequest イベントをサブスクライブすると、接続されたモジュールまたはイベント ハンドラープログラム

PostAuthenticateRequest

を処理する前にリクエストが認証されることが保証されます。セキュリティ モジュールがユーザー ID を確立したときに発生します。 PostAuthenticateRequest イベントは、AuthenticateRequest イベントの発生後に発生します。 PostAuthenticateRequest イベントをサブスクライブする関数は、PostAuthenticateRequest によって処理されるすべてのデータにアクセスできます。

AuthorizeRequest

セキュリティ モジュールがユーザーの承認を検証したときに発生します。 AuthorizeRequest イベントは、ASP.NET が現在の要求を承認したことを通知します。 AuthorizeRequest イベントをサブスクライブすると、接続されたモジュールまたはイベント ハンドラーが処理される前にリクエストが認証および承認されるようになります。

PostAuthorizeRequest

は、現在のリクエストのユーザーが承認されると発生します。 PostAuthorizeRequest イベントは、ASP.NET が現在の要求を承認したことを通知します。 PostAuthorizeRequest イベントをサブスクライブすると、接続されたモジュールまたはハンドラーが処理される前に要求が認証および承認されるようになります

ResolveRequestCache

ASP.NET が Authorize イベントを完了してキャッシュ モジュールからの要求を処理するときキャッシュが発生すると、イベント ハンドラー (ページや XML Web サービスなど) の実行がスキップされます。ASP.NET の PostResolveRequestCache

AcquireRequestState

PreRequestHandlerExecute

の直前に発生します。 ASP.NET がイベント ハンドラー (ページや XML Web サービスなど) の実行を開始します

PostRequestHandlerExecute

ASP.NET イベント ハンドラー (ページや XML Web サービスなど) が実行されると発生します。 ) の実行が完了しました

ReleaseRequestState

ASP.NET によってすべての要求が実行された後、このイベントは状態モジュールに現在の状態データを保存させます。

PostReleaseRequestState

PostUpdateRequestCache

このイベントは、ASP.NET がキャッシュ モジュールの更新を完了し、キャッシュからの後続の要求を処理するための応答を保存した後に発生します。

LogRequest

このイベントは、ASP.NET がキャッシュ モジュールの更新を完了し、キャッシュからの後続の要求を処理するための応答を保存した後に発生します。

このイベントは、IIS 7.0 が統合モードで、.NET Framework がバージョン 3.0 以上である場合にのみサポートされます。

PostLogRequest

ASP.NET が LogRequest イベントのすべてのイベント ハンドラーを処理した後に発生します。

このイベントは、IIS 7.0 が統合モードで、.NET Framework がバージョン 3.0 以上である場合にのみサポートされます。

EndRequest

は、ASP.NET が要求に応答するときに、HTTP 実行パイプライン チェーンの最後のイベントとして発生します。

EndRequest イベントは、CompleteRequest メソッドが呼び出されるときに常に発生します。

ASP.NET アプリケーションの場合、HttpApplication は Global.aspx から派生します (作成したアプリケーションには Global.aspx ファイルがあることがわかります)。これらの 19 のいずれかを挿入して HttpApplication リクエストをカスタマイズできます。論理処理のためのイベント。 Global.aspx では、「Application_{イベント名}」というメソッド名に従ってイベントを登録します。

イベント名は上記19件のイベント名です。たとえば、Application_EndRequest は、アプリケーションの EndRequest イベントを処理するために使用されます。

HttpModule

ASP.NET には拡張性の高いエンジンがあり、さまざまなリソース タイプのリクエストを処理できます。これはHTTPモジュールです。要求が ASP.net パイプラインに転送されると、リソースに一致する HttpHandler オブジェクトが最終的に要求の処理を担当しますが、HttpHandler が処理される前に、ASP.NET はまず、構成されているすべての HttpModule オブジェクトを読み込んで初期化します。 HttpModule が初期化されると、一部のコールバック イベントが HttpApplication の対応するイベントに挿入されます。すべての HttpModule は、Init メソッドを持つ IHttpModule インターフェイスを実装します。


public interface IHttpModule
{
 // Methods
 void Dispose();
 void Init(HttpApplication context);
}
ログイン後にコピー


このオブジェクトを使用すると、Init メソッドが HttpApplication オブジェクトを受け入れることができ、HttpApplication の 19 個のイベントの 1 つを簡単に登録できることがわかります。このように、HttpApplication オブジェクトがイベントを実行すると、イベントは自然に開始されます。

HttpHandler

異なるリソースタイプのリクエストの場合、ASP.NET はそれらを処理するために異なる HttpHandler をロードします。すべての HttpHandler は IhttpHandler インターフェイスを実装します。


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

 // Properties
 bool IsReusable { get; }
}
ログイン後にコピー


このインターフェースには ProcessRequest メソッドがあることがわかります。その名前が示すように、このメソッドは主にリクエストを処理するために使用されます。したがって、各リクエストは最終的に、対応する HttpHandler に分散されてリクエストを処理します。

ASP.NET MVC の動作メカニズム

さて、上で多くのことを述べてきましたが、実際にはこれに向けた道が開かれています。ようやく本題に到達しました。まず、MVC が経験する主なパイプライン イベントを説明している下の図を見てください。

上の図は、http リクエストから完全な MVC アプリケーションの応答までのプロセス全体です。リクエストをインターセプトする UrlRoutingModule から、ExecuteResult メソッドを実行してレスポンスを生成する最後の ActionResult まで。

それでは、これらのプロセスが何を行うのか詳しく説明しましょう。

UrlRoutingModule

MVC アプリケーション UrlRoutingModule のエントリ ポイント

前に述べたように、ASP.NET は HttpModule オブジェクトの初期化イベント Init を読み込み、すべての HttpModule オブジェクトは IHttpModule インターフェイスを実装します。 。 UrlRoutingModule の実装を見てみましょう:

上の図から、リクエストが ASP.NET パイプラインに転送されるときに、UrlRoutingModule がインターフェイス IHttpModule の Init() メソッドを実装していることがわかります。 UrlRoutingModule オブジェクトがロードされます。

それでは、なぜ UrlRoutingModule がロードされ、初期化されるのでしょうか?なぜ別の HttpModule オブジェクトを使用しないのでしょうか?この疑問を念頭に置いて話を続けます。

ASP.NET MVC のコアは間違いなく "ルーティング システム" であり、ルーティング システムのコアは強力な System.Web.Routing.dll コンポーネントから来ています。 System.Web.Routing.dll は MVC に固有のものではありませんが、MVC フレームワークは MVC から切り離せません。

まず、UrlRoutingModule がどのように機能するかを理解する必要があります。

(1) IIS Web サイトの構成は、グローバル Web.config とローカル Web.config の 2 つのブロックに分けることができます。 Asp.Net ルーティングはグローバルであるため、グローバル Web.Config で構成されています。「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>
ログイン後にコピー


上で赤でマークした行が見えましたか:

UrlRoutingModule は一意ではありませんMVC へ はい、これはグローバル構成です。つまり、すべての ASP.NET リクエストがここに到着するため、モジュールはそれが MVC リクエストであるか WebForm リクエストであるかを最終決定できません。しかし、重要な場所でもあります。

(2) グローバル Web.Config に System.Web.Routing.UrlRoutingModule を登録すると、IIS 要求処理パイプラインがリクエストを受信した後、UrlRoutingModule 型の Init() メソッドを読み込みます。ソース コードは次のとおりです。


[TypeForwardedFrom("System.Web.Routing, Version=3.5.0.0, Culture=Neutral, PublicKeyToken=31bf3856ad364e35")]
public class UrlRoutingModule : IHttpModule
{
 // Fields
 private static readonly object _contextKey = new object();
 private static readonly object _requestDataKey = new object();
 private RouteCollection _routeCollection;

 // Methods
 protected virtual void Dispose()
 {
 }

 protected virtual void Init(HttpApplication application)
 {
 if (application.Context.Items[_contextKey] == null)
 {
 application.Context.Items[_contextKey] = _contextKey;
 application.PostResolveRequestCache += new EventHandler(this.OnApplicationPostResolveRequestCache);
 }
 }

 private void OnApplicationPostResolveRequestCache(object sender, EventArgs e)
 {
 HttpApplication application = (HttpApplication) sender;
 HttpContextBase context = new HttpContextWrapper(application.Context);
 this.PostResolveRequestCache(context);
 }

 [Obsolete("This method is obsolete. Override the Init method to use the PostMapRequestHandler event.")]
 public virtual void PostMapRequestHandler(HttpContextBase context)
 {
 }

 public virtual void PostResolveRequestCache(HttpContextBase context)
 {
 RouteData routeData = this.RouteCollection.GetRouteData(context);
 if (routeData != null)
 {
 IRouteHandler routeHandler = routeData.RouteHandler;
 if (routeHandler == null)
 {
 throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, SR.GetString("UrlRoutingModule_NoRouteHandler"), new object[0]));
 }
 if (!(routeHandler is StopRoutingHandler))
 {
 RequestContext requestContext = new RequestContext(context, routeData);
 context.Request.RequestContext = requestContext;
 IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);
 if (httpHandler == null)
 {
  throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, SR.GetString("UrlRoutingModule_NoHttpHandler"), new object[] { routeHandler.GetType() }));
 }
 if (httpHandler is UrlAuthFailureHandler)
 {
  if (!FormsAuthenticationModule.FormsAuthRequired)
  {
  throw new HttpException(0x191, SR.GetString("Assess_Denied_Description3"));
  }
  UrlAuthorizationModule.ReportUrlAuthorizationFailure(HttpContext.Current, this);
 }
 else
 {
  context.RemapHandler(httpHandler);
 }
 }
 }
 }

 [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
 void IHttpModule.Dispose()
 {
 this.Dispose();
 }

 [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
 void IHttpModule.Init(HttpApplication application)
 {
 this.Init(application);
 }

 // Properties
 public RouteCollection RouteCollection
 {
 get
 {
 if (this._routeCollection == null)
 {
 this._routeCollection = RouteTable.Routes;
 }
 return this._routeCollection;
 }
 [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
 set
 {
 this._routeCollection = value;
 }
 }
}
ログイン後にコピー


上記の UrlRoutingModule ソース コードで Init メソッドがどのように実装されているかを見てください。 Init() メソッドの赤い場所にマークを付けました:

application.PostResolveRequestCache +=。新しい EventHandler(this .OnApplicationPostResolveRequestCache);

这一步至关重要哈,看到没有,就是对我们在HttpApplication那19个事件中的PostResolveRequestCache事件的注册。注册的方法是OnApplicationPostResolveRequestCache事件。也就是说HttpApplication对象在执行到PostResolveRequestCache这个事件的时候,就会执行OnApplicationPostResolveRequestCache事件。决定是MVC机制处理请求的关键所在就是OnApplicationPostResolveRequestCache事件。

从源码中我们看出,OnApplicationPostResolveRequestCache事件执行的时候,最终执行了PostResolveRequestCache这个方法。最关键的地方呢就在这里了。

当请求到达UrlRoutingModule的时候,UrlRoutingModule取出请求中的Controller、Action等RouteData信息,与路由表中的所有规则进行匹配,若匹配,把请求交给IRouteHandler,即MVCRouteHandler。我们可以看下UrlRoutingModule的源码来看看,以下是几句核心的代码:

我们再分析一下这个方法的源码:


public virtual void PostResolveRequestCache(HttpContextBase context)
{
 // 通过RouteCollection的静态方法GetRouteData获取到封装路由信息的RouteData实例
 RouteData routeData = this.RouteCollection.GetRouteData(context);
 if (routeData != null)
 {
 // 再从RouteData中获取MVCRouteHandler
 IRouteHandler routeHandler = routeData.RouteHandler;
 ......
 if (!(routeHandler is StopRoutingHandler))
 {
 ......
 // 调用 IRouteHandler.GetHttpHandler(),获取的IHttpHandler 类型实例,它是由 IRouteHandler.GetHttpHandler获取的,这个得去MVC的源码里看
 IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);
 ......
 // 合适条件下,把之前将获取的IHttpHandler 类型实例 映射到IIS HTTP处理管道中
 context.RemapHandler(httpHandler);
 }
 }
}
ログイン後にコピー


看到了吧,通过路由规则,返回的不为空,说明匹配正确,关于路由规则的匹配,说起来也不短,这里就不大幅介绍,有时间下次再开篇详解路由机制。匹配成功后,返回一个RouteData类型的对象,RouteData对象都有些什么属性呢?看看这行源码: IRouteHandler routeHandler = routeData.RouteHandler;或者看源码我们知道,RouteDate有一个RouteHandler属性。

那么UrlRouting Module是如何选择匹配规则的呢?

我们看看我们新建的MVC应用程序,在App_Start文件夹下面有一个RouteConfig.cs类,这个类的内容如下:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;

namespace ApiDemo
{
 public class RouteConfig
 {
 public static void RegisterRoutes(RouteCollection routes)
 {
 routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

 routes.MapRoute(
 name: "Default",
 url: "{controller}/{action}/{id}",
 defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
 );
 }
 }
}
ログイン後にコピー


我们在这个类里面,主要是给路由表添加路由规则。在看看上面的UrlRoutingModule类,里面有一个RoutCollection属性,所以UrlRoutingModule能够获取路由表中的所有规则,这里值得注意的是,路由规则的匹配是有顺序的,如果有多个规则都能够匹配,UrlRoutingModule至选择第一个匹配的规则就返回,不再继续往下匹配了。相反的如果一个请求,没有匹配到任何路由,那么该请求就不会被处理。

这里返回的RouteData里的RouteHandler就是MVCRouteHandler。为什么呢?那我们继续往下看RouteHandler。

RouteHandler

生成MvcHander

在上面路由匹配的过程中,与匹配路由相关联的MvcRouteHandler ,MvcRouteHandler 实现了IRouteHandler 接口。MvcRouteHandler 主要是用来获取对MvcHandler的引用。MvcHandler实现了IhttpHandler接口。

MVCRouteHandler的作用是用来生成实现IHttpHandler接口的MvcHandler。而我们前面说过最终处理请求的都是相对应的HttpHandler。那么处理MVC请求的自然就是这个MvcHandler。所以这里返回MvcRouteHandler至关重要:

那么,MvcRouteHandler从何而来呢?众所周知,ASP.NET MVC项目启动是从Global中的Application_Start()方法开始的,那就去看看它:


public class MvcApplication : System.Web.HttpApplication
 {
 protected void Application_Start()
 {

 RouteConfig.RegisterRoutes(RouteTable.Routes);
 BundleConfig.RegisterBundles(BundleTable.Bundles);
 }
 }

 public class RouteConfig
 {
 public static void RegisterRoutes(RouteCollection routes)
 {
 routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
System.Web.Mvc.RouteCollectionExtensions
 routes.MapRoute(
 name: "Default",
 url: "{controller}/{action}/{id}",
 defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
 );
 }
 }
ログイン後にコピー


看看我上面标红的代码:这是路由注册,玄机就在这里。那我们去看看MapRoute源码就知道咯:


public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces) 
 {
 ......

 Route route = new Route(url, new MvcRouteHandler()) {
 Defaults = new RouteValueDictionary(defaults),
 Constraints = new RouteValueDictionary(constraints),
 DataTokens = new RouteValueDictionary()
 };
 ......
 return route;
 }
ログイン後にコピー


看看我们5-8行代码,在MVC应用程序里,在路由注册的时候,我们就已经给他一个默认的HttpRouteHandler对象,就是 New MvcRouteHandler().现在我们反推回去,我们MVC程序在路由注册的时候就已经确定了HttpRouteHandler为MvcRouteHandler,那么当我们在前面PostResolveRequestCache方法里,当我们的请求与路由匹配成功后,自然会返回的是MvcRouteHandler。

好啦,MvcRouteHandler生成了。那么MvcRouteHandler能做什么呢?又做了什么呢?

再回头看看 PostResolveRequestCache方法,在成功获取到IHttpRouteHandler对象即MvcRouteHandler之后,又做了下面这一个操作:

IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);

我们看看这个IHttpHandler 的源码:


namespace System.Web.Routing
{ 
 public interface IRouteHandler
 { 
 IHttpHandler GetHttpHandler(RequestContext requestContext);
 }
}
ログイン後にコピー


有一个GetHttpHandler的方法,恰好就调用了这个方法。那我们看看MvcRouteHandler是怎么实现这个GetHttpHandler的呢:


public class MvcRouteHandler : IRouteHandler
{
 // Fields
 private IControllerFactory _controllerFactory;

 // Methods
 public MvcRouteHandler()
 {
 }

 public MvcRouteHandler(IControllerFactory controllerFactory)
 {
 this._controllerFactory = controllerFactory;
 }

 protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext)
 {
 requestContext.HttpContext.SetSessionStateBehavior(this.GetSessionStateBehavior(requestContext));
 return new MvcHandler(requestContext);
 }

 protected virtual SessionStateBehavior GetSessionStateBehavior(RequestContext requestContext)
 {
 string str = (string) requestContext.RouteData.Values["controller"];
 if (string.IsNullOrWhiteSpace(str))
 {
  throw new InvalidOperationException(MvcResources.MvcRouteHandler_RouteValuesHasNoController);
 }
 IControllerFactory factory = this._controllerFactory ?? ControllerBuilder.Current.GetControllerFactory();
 return factory.GetControllerSessionBehavior(requestContext, str);
 }

 IHttpHandler IRouteHandler.GetHttpHandler(RequestContext requestContext)
 {
 return this.GetHttpHandler(requestContext);
 }
}
ログイン後にコピー


看第16-20行代码,这时候应该明白了吧。顺理成章的返回了MvcHandler对象。记得我们前面说过,请求最终是被相对应的HttpHander对象处理的。MvcHandler就是那个用来处理Mvc请求的HttpHandler。MvcRouteHandler把请求交给了MvcHandler去做请求处理管道中后续事件的处理操作了。

下面我们就看看MvcHandler做了些什么:

MvcHandler

MvcHandler就是最终对request进行处理。

MvcHandler的定义如下:

我们可以看到MvcHandler就是一个普通的Http Handler.我们知道一个http handler需要实现一个ProcessRequest()的方法,这个方法就是处理request的核心。所以MvcHandler实现了ProcessRequest()方法。

ProcessRequest主要功能:

(1)在ASP.NET MVC中,会调用MvcHandler的ProcessRequest()方法,此方法会激活具体请求的Controller类对象,触发Action方法,返回ActionResult实例。

(2)如果ActionResult是非ViewResult,比如JsonResult, ContentResult,这些内容将直接被输送到Response响应流中,显示给客户端;如果是ViewResult,就会进入下一个渲染视图环节。

(3)在渲染视图环节,ViewEngine找到需要被渲染的视图,View被加载成WebViewPage类型,并渲染生成Html,最终返回Html。

ProcessRequest()定义如下:


// Copyright (c) Microsoft Open Technologies, Inc.<pre class="brush:php;toolbar:false">// All rights reserved. See License.txt in the project root for license information.
void IHttpHandler.ProcessRequest(HttpContext httpContext) 
{
 ProcessRequest(httpContext);
}
protected virtual void ProcessRequest(HttpContext httpContext) 
{
 HttpContextBase iHttpContext = new HttpContextWrapper(httpContext);
  ProcessRequest(iHttpContext);
}
protected internal virtual void ProcessRequest(HttpContextBase httpContext) {
 SecurityUtil.ProcessInApplicationTrust(() => {
 IController controller;
 IControllerFactory factory;
 ProcessRequestInit(httpContext, out controller, out factory);
 try
 {
 controller.Execute(RequestContext);
 }
 finally
 {
 factory.ReleaseController(controller);
 }
 });
}
ログイン後にコピー


从上面的代码可以看出调用了一个ProcessRequestInit()方法,定义如下:


private void ProcessRequestInit(HttpContextBase httpContext, 
  out IController controller, out IControllerFactory factory) {
 // If request validation has already been enabled, make it lazy.
 // This allows attributes like [HttpPost] (which looks
 // at Request.Form) to work correctly without triggering full validation.
 bool? isRequestValidationEnabled = 
 ValidationUtility.IsValidationEnabled(HttpContext.Current);
 if (isRequestValidationEnabled == true) {
 ValidationUtility.EnableDynamicValidation(HttpContext.Current);
 }
 AddVersionHeader(httpContext);
 RemoveOptionalRoutingParameters();
 // Get the controller type
 string controllerName = RequestContext.RouteData.GetRequiredString("controller");
 // Instantiate the controller and call Execute
 factory = ControllerBuilder.GetControllerFactory();
 controller = factory.CreateController(RequestContext, controllerName);
 if (controller == null) {
 throw new InvalidOperationException(
 String.Format(
  CultureInfo.CurrentCulture,
  MvcResources.ControllerBuilder_FactoryReturnedNull,
  factory.GetType(),
  controllerName));
 }
}
ログイン後にコピー


在ProcessRequestInit()方法中首先创建了ControllerFactory()的对象 factory.然后ControllerFactory创建了相关Controller的实例.最终调用了Controller的Excute()方法。

好我们再来看看ControllerFactory:

ControllerFactory

主要是用来生成Controller对象

ControllerFactory实现了接口IControllerFactory.

Controller

到这里我们大概就知道了,MvcHandler通过ProcessRequest()方法最终创建了Controller对象,这里我们都应该知道,Controller里面包含很多的Action方法,每一次请求至少一个Action方法会被调用。为了明确的实现IController接口,框架里面有一个ControllerBase的类已经实现了IController接口,其实我们自己的Controller也可以不继承ControllerBase,只要实现IController接口即可。


public abstract class ControllerBase : IController
{
 protected virtual void Execute(RequestContext requestContext)
 {
 if (requestContext == null)
 {
  throw new ArgumentNullException("requestContext");
 }
 if (requestContext.HttpContext == null)
 {
  throw new ArgumentException(
  MvcResources.ControllerBase_CannotExecuteWithNullHttpContext, 
  "requestContext");
 }
 VerifyExecuteCalledOnce();
 Initialize(requestContext);
 using (ScopeStorage.CreateTransientScope())
 {
  ExecuteCore();
 }
 }
 protected abstract void ExecuteCore(); 
 // .......
ログイン後にコピー


controller对象实际上使用ActionInvoker来调用Action方法的,当Controller对象被创建后,会执行Controller对象的基类ControllerBase类里面的Excute方法。Excute方法又调用了ExcuteCore()方法。Controller类里面实现了ExcuteCore()方法。ExcuteCore调用了ActionInvoker的InvokerAction方法来调用Action方法。

ActionInvoker

ActionInvoker方法有很重要的责任来查找Controller中的Action方法并且调用。

ActionInvoker是一个实现了IActionInvoker接口的对象:


bool InvokeAction(
  ControllerContext controllerContext,
  string actionName
)
ログイン後にコピー


Controller类里面暴露了一个ActionInvoker 属性,会返回一个ControllerActionInvoker 。ActionInvoker通过CreateActionInvoker()方法来创建ControllerActionInvoker对象。


public IActionInvoker ActionInvoker {
 get {
 if (_actionInvoker == null) {
  _actionInvoker = CreateActionInvoker();
 }
 return _actionInvoker;
 }
 set {
 _actionInvoker = value;
 }
}
 protected virtual IActionInvoker CreateActionInvoker() {
 return new ControllerActionInvoker();
}
ログイン後にコピー


我们看到CreateActionInvoker()是一个Virtual方法,我们可以实现自己的ActionInvoker.

ActionInvoker类需要匹配Controller中详细的Action来执行,而这些详细的信息是由ControllerDescriptor 提供的。ControllerDescriptor 和ActionDescriptor在ActionInvoker中扮演重要的角色。这两个分别是对Controler和Action的详细描述。ControllerDescriptor 描述了Controller的相关信息比如name,action,type等。

ActionDescriptor 描述了Action相关的详情,比如name,controller,parameters,attributes和fiflters等。

ActionDescriptor 中一个中要的方法就是FindAction(),这个方法返回一个ActionDescriptor 对象,所以ActionInvoker知道该调用哪个Action。

ActionResult

到目前为止,我们看到了Action方法被ActionInvoker调用。所有的Action方法有一个特性,就是返回一个ActionResult类型的数据。


public abstract class ActionResult
 {
 public abstract void ExecuteResult(ControllerContext context);
 }
ログイン後にコピー


ExecuteResult()是一个抽象方法,所以不同的子类可以提供不同的ExecuteResult()实现。

ActionResult执行后响应输出到客户端。

ViewEngine

ViewResult几乎是大部分应用程序的返回类型,主要通过ViewEngine引擎来展示view的。ViewEngine可能主要就是生成Html元素的引擎。Framwork提供了2种引擎,Razor View Engine 和Web Form View Engine.如果你想自定义引擎,你可以创建一个引擎只要实现IViewEngine接口即可。

IViewEngine 有下面几个方法:

1、FindPartialView :当controller需要返回一个PartialView的时候,FindPartialView方法 就会被调用。

2、FindView

3、ReleaseView :主要用来有ViewEngine释放资源

ViewResultBase 和ViewResult是比较重要的两个类。ViewResultBase 包含下面的实现代码:


if (View == null)
  {
  result = FindView(context); //calls the ViewResult&#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方法介绍

以上がasp.net MVCアプリケーションのライフサイクル(詳細解説)_実践的なヒントの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート
私たちについて 免責事項 Sitemap
PHP中国語ウェブサイト:福祉オンライン PHP トレーニング,PHP 学習者の迅速な成長を支援します!