>  기사  >  웹 프론트엔드  >  asp.net MVC 애플리케이션의 라이프사이클(상세설명)_실용팁

asp.net MVC 애플리케이션의 라이프사이클(상세설명)_실용팁

韦小宝
韦小宝원래의
2017-12-15 14:23:181537검색

아래 편집자는 asp.net MVC 애플리케이션을 기반으로 라이프 사이클에 대한 자세한 설명을 공유할 것입니다. 이는 좋은 참조 가치가 있으며 모든 사람에게 도움이 되기를 바랍니다. asp.net에 관심이 있다면 편집자를 따라 살펴보세요

우선, 우리는 http가 상태 비저장 요청이고 클라이언트 브라우저가 요청을 보낼 때 시작되고 응답이 있을 때 끝나는 수명 주기를 알고 있습니다. 받았다. 그렇다면 MVC 애플리케이션은 요청부터 응답까지 어떤 작업을 수행할까요?

이 기사에서는 MVC 애플리케이션의 요청 수명 주기와 요청이 한 컨트롤에서 다른 컨트롤로 처리되는 방법에 대해 자세히 설명합니다. 또한 전체 요청 수명주기에서 사용되는 관련 구성 요소를 자세히 소개합니다. 왜냐하면 일반적인 개발 과정에서 우리는 MVC 프레임워크를 사용하여 관련 요청을 처리하는 방법을 알 수 있지만 대부분의 경우 컨트롤러와 작업 메서드 간의 관련 처리만 수행하며 실제 내부 작동 메커니즘에 대해서는 많이 알지 못할 수 있습니다. 실제로 내부 메커니즘을 어느 정도 이해하면 Microsoft의 MVC 프레임워크는 확장성이 매우 뛰어나며 확장 인터페이스를 통해 확장을 통해 필요한 처리 메커니즘을 정의할 수 있습니다. 이것이 MVC가 필요한 이유입니다. 프레임워크가 너무 유명해요.

처음 mvc 사용법을 배울 때 저를 괴롭혔던 질문은 요청의 프로세스 제어가 무엇인가요?였습니다. 뷰부터 컨트롤러, 액션까지 어떤 경험을 하셨나요? 당시에는 요청을 처리하는 데 HTTP 모듈과 HTTP 핸들러가 어떤 역할을 하는지 몰랐습니다. 결국 MVC는 웹 개발 프레임워크이므로 전체 요청 처리 프로세스에는 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 클래스의 인스턴스를 사용하여 수명 동안 수신된 많은 요청을 처리합니다. 그러나 한 번에 하나의 요청만 처리할 수 있습니다. 이런 방식으로 멤버 변수를 사용하여 각 요청에 대한 데이터를 저장할 수 있습니다.

이는 MVC든 WebForm이든 ASP.NET 애플리케이션이 결국 HttpApplication 클래스의 인스턴스에 도달한다는 의미입니다. HttpApplication은 전체 ASP.NET 인프라의 핵심이며 배포된 요청을 처리하는 역할을 담당합니다. HttpApplication의 요청 처리 주기는 전체 프로세스 중에 해당 이벤트가 여러 단계에서 트리거되는 복잡한 프로세스입니다. 해당 이벤트를 등록하고 HttpApplication 처리 요청의 특정 단계에 처리 논리를 주입할 수 있습니다. HttpApplication 인스턴스에 도착하는 요청을 처리하기 위해 HttpApplication 클래스에는 19개의 이벤트가 정의되어 있습니다. 즉, MVC나 WebForm에 관계없이 결국에는 이 19개의 이벤트가 처리됩니다. 그러면 방금 언급한 요청 처리 메커니즘을 제외하면 MVC와 WebFrom은 대부분 동일합니다. 그들은 어디에서 각자의 길을 가기 시작했습니까? 아마도 이 19가지 방법 중 하나일 것입니다. 계속해서 아래를 살펴보겠습니다.

다음 19개의 이벤트를 살펴보겠습니다.

애플리케이션은 global.asax 파일에 정의된 모듈이나 사용자 코드에서 처리하는 이벤트를 다음 순서로 실행합니다.


은 현재 이벤트 핸들러의 실행을 건너뛰고 캐시 모듈이 요청 충족 캐시가 요청될 때 발생합니다. )는 PostResolveRequestCache 이벤트 이후 PostMapRequestHandler 이벤트 이전에 이벤트 핸들러(요청 URL에 해당하는 페이지)를 생성합니다. PostMapRequestHandler는 ASP.NET이 현재 요청을 해당 이벤트 핸들러 에 매핑할 때 발생합니다. AcquireRequestStateASP.NET이 현재 요청과 관련된 현재 상태(예: 세션 상태)를 가져올 때 발생합니다. PostReleaseRequestStateUpdateRequestCachePostUpdateRequestCache

이벤트 이름:

간단한 설명:

BeginRequest

ASP.NET은 응답할 때 HTTP 실행 파이프라인 체인의 첫 번째 역할을 합니다. 요청에 이벤트가 발생합니다

AuthenticateRequest

보안 모듈이 사용자 ID를 설정하면 발생합니다. 참고: AuthenticateRequest 이벤트는 구성된 인증 메커니즘이 현재 요청을 인증했음을 알립니다. AuthenticateRequest 이벤트를 구독하면 연결된 모듈 또는 이벤트 핸들러프로그램

PostAuthenticateRequest

을 처리하기 전에 요청이 인증됩니다. 보안 모듈이 사용자 ID를 설정했을 때 발생합니다. PostAuthenticateRequest 이벤트는 AuthenticateRequest 이벤트가 발생한 후에 발생합니다. PostAuthenticateRequest 이벤트를 구독하는 함수는 PostAuthenticateRequest가 처리하는 모든 데이터에 액세스할 수 있습니다.

AuthorizeRequest

보안 모듈이 사용자 인증을 확인한 경우 발생합니다. AuthorizeRequest 이벤트는 ASP.NET이 현재 요청을 승인했음을 알립니다. AuthorizeRequest 이벤트를 구독하면 연결된 모듈이나 이벤트 핸들러가 처리되기 전에 요청이 인증되고 승인됩니다. PostAuthorizeRequest 이벤트는 ASP.NET이 현재 요청을 승인했음을 알립니다. PostAuthorizeRequest 이벤트를 구독하면 연결된 모듈이나 핸들러가 처리되기 전에 요청이 인증되고 승인됩니다.

ResolveRequestCache

ASP.NET이 Authorize 이벤트를 완료하여 캐시 모듈이 캐시가 발생하여 이벤트 핸들러(예: 페이지 또는 XML 웹 서비스)의 실행을 건너뜁니다. ASP.NET의 PostResolveRequestCache

PreRequestHandlerExecute
직전에 발생합니다. ASP.NET은 이벤트 처리기(예: 페이지 또는 XML 웹 서비스) 실행을 시작합니다.

PostRequestHandlerExecute
ASP.NET 이벤트 처리기(예: 페이지 또는 XML 웹 서비스)가 실행될 때 발생합니다. )이 실행을 완료했습니다.

ReleaseRequestState

모든 요청이 ASP.NET에 의해 실행된 후 이 이벤트는 상태 모듈이 현재 상태 데이터를 저장하도록 합니다.

ASP.NET이 모든 요청 이벤트 핸들러 실행을 완료하고 요청 상태 데이터가 저장되었을 때 발생합니다.

ASP.NET이 이벤트 핸들러 실행을 완료했을 때 발생합니다. 캐시 모듈은 캐시의 후속 요청을 처리하는 데 사용될 응답을 저장합니다

이 이벤트는 ASP.NET이 캐시 모듈 업데이트를 완료하고 캐시의 후속 요청을 처리하기 위한 응답을 저장한 후에 발생합니다.

LogRequest

이 이벤트는 ASP.NET이 캐시 모듈 업데이트를 완료하고 캐시에서 후속 요청을 제공하기 위한 응답을 저장한 후에 발생합니다.

이 이벤트는 IIS 7.0이 통합 모드이고 .NET Framework가 버전 3.0 이상인 경우에만 지원됩니다.

PostLogRequest

ASP.NET이 LogRequest 이벤트에 대한 모든 이벤트 처리기를 처리한 후에 발생합니다.

이 이벤트는 IIS 7.0이 통합 모드이고 .NET Framework 버전이 3.0 이상인 경우에만 지원됩니다.

EndRequest

은 ASP.NET이 요청에 응답할 때 HTTP 실행 파이프라인 체인의 마지막 이벤트로 발생합니다.

CompleteRequest 메서드가 호출되면 EndRequest 이벤트가 항상 발생합니다.

ASP.NET 애플리케이션의 경우 HttpApplication은 Global.aspx에서 파생됩니다(우리가 만드는 애플리케이션에 Global.aspx 파일이 있음을 볼 수 있음). Global.aspx 파일에 HttpApplication 요청을 사용자 정의할 수 있습니다. 논리적 처리를 위한 이벤트. Global.aspx에서는 "Application_{Event Name}" 메서드에 따라 이벤트 이름을 지정합니다.

이벤트 이름은 위 19개 이벤트의 이름입니다. 예를 들어, Application_EndRequest는 Application의 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개 이벤트 중 하나를 쉽게 등록할 수 있습니다. 이런 식으로 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

First는 요청을 시작합니다. 앞에서 언급했듯이 ASP.NET은 HttpModule 개체의 초기화 이벤트 Init를 로드하고 모든 HttpModule 개체는 IHttpModule 인터페이스를 구현합니다. UrlRoutingModule의 구현을 살펴보겠습니다.

위 그림에서 UrlRoutingModule은 요청이 ASP.NET 파이프라인으로 전송될 때 Init() 메서드를 구현하는 것을 볼 수 있습니다. UrlRoutingModule 개체가 로드됩니다.

그렇다면 UrlRoutingModule이 로드되고 초기화되는 이유는 무엇일까요? 다른 HttpModule 객체는 왜 안되나요? 이 질문을 염두에 두고 계속하겠습니다.

ASP.NET MVC에서 핵심은 의심할 여지 없이 "라우팅 시스템"이며 라우팅 시스템의 핵심은 강력한 System.Web.Routing.dll 구성 요소에서 나옵니다. System.Web.Routing.dll 은 MVC에 고유하지 않지만 MVC 프레임워크는 MVC와 분리될 수 없습니다.

먼저 UrlRoutingModule이 어떻게 작동하는지 이해해야 합니다.

(1) IIS 웹 사이트의 구성은 전역 Web.config와 로컬 Web.config의 두 블록으로 나눌 수 있습니다. 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>


위에 빨간색으로 표시한 줄을 보셨나요? f5b4e48a00c3d1f9c7a5f488c13663e9

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被加载成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方法介绍

위 내용은 asp.net MVC 애플리케이션의 라이프사이클(상세설명)_실용팁의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.