asp.net MVC 应用程序的生命周期(下)

看看上面的UrlRoutingModule源码里面是怎么实现Init方法的,Init()方法里面我标注红色的地方:

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<TModel>类型,并渲染生成Html,最终返回Html。

ProcessRequest()定义如下:

// Copyright (c) Microsoft Open Technologies, Inc.<pre>// 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输出响应。

总结:如果我们理解了整个过程中发生了什么,哪些类和哪些方法被调用,我们就可以在需要扩展的地方轻松的进行扩展。

时间: 2024-10-16 00:28:29

asp.net MVC 应用程序的生命周期(下)的相关文章

asp.net MVC 应用程序的生命周期(上)

首先我们知道http是一种无状态的请求,他的生命周期就是从客户端浏览器发出请求开始,到得到响应结束.那么MVC应用程序从发出请求到获得响应,都做了些什么呢? 本文我们会详细讨论MVC应用程序一个请求的生命周期,从一个控件到另一个控件是怎样被处理的.我们还会详细介绍一下整个请求的生命周期中,用到的相关组件.因为在平常的开发过程中,我们可能知道怎样去使用MVC框架来处理相关的请求,大部分的时候我们只是在controller和action方法之间做相关的处理,对于真正内在的运行机制可能不是很了解.其实

asp.net MVC 应用程序的生命周期

首先我们知道http是一种无状态的请求,他的生命周期就是发出请求开始,到得到响应结束.那么MVC应用程序从发出请求到获得响应,都做了些什么呢? 本文我们会详细讨论MVC应用程序的生命周期和一个请求,从一个控件到另一个控件是怎样被处理的.我们还会详细介绍一下整个请求的生命周期中,用到的相关组件.在平常的开发过程中,我们可能知道怎样去使用MVC框架来处理相关的请求,大部分的时候我们只是在controller和action方法之间做相关的处理. 当我最开始学习使用mvc的时候,困扰我的一个问题就是,一

ASP.NET 应用程序(Application)生命周期概述

原文:ASP.NET 应用程序(Application)生命周期概述 引用MSDN:ASP.NET 应用程序生命周期概述 本 主题概述应用程序生命周期,列出重要的生命周期事件,并描述如何编写适合应用程序生命周期的代码.在 ASP.NET 中,若要对 ASP.NET 应用程序进行初始化并使它处理请求,必须执行一些处理步骤.此外,ASP.NET 只是对浏览器发出的请求进行处理的 Web 服务器结构的一部分.了解应用程序生命周期非常重要,这样才能在适当的生命周期阶段编写代码,达到预期的效果. 应用程序

使用Metrics.NET 构建 ASP.NET MVC 应用程序的性能指标

通常我们需要监测ASP.NET MVC 或 Web API 的应用程序的性能时,通常采用的是自定义性能计数器,性能计数器会引发无休止的运维问题(损坏的计数器.权限问题等).这篇文章向你介绍一个新的替代性能计数器的工具Metrics.NET,因为是它是内部的,所以我们能够向系统中添加更多更有意义的度量标准. Metrics.NET(https://github.com/etishor/Metrics.NET)是一个给CLR 提供度量工具的包,它是移植自Java的metrics,支持的平台 .NET

ASP.NET MVC5请求管道和生命周期

请求处理管道 请求管道是一些用于处理HTTP请求的模块组合,在ASP.NET中,请求管道有两个核心组件:IHttpModule和IHttpHandler.所有的HTTP请求都会进入IHttpHandler,有IHttpHandler进行最终的处理,而IHttpModule通过订阅HttpApplication对象中的事件,可以在IHttpHandler对HTTP请求进行处理之前对请求进行预处理或IHttpHandler对HTTP请求处理之后进行再次处理. 在IIS7之前,如IIS6或IIS5,请

[转载] iOS应用程序的生命周期

iOS应用程序的生命周期 2015-06-23 iOS大全 (点击上方蓝字,快速关注我们) iOS应用程序一般都是由自己编写的代码和系统框架(system frameworks)组成,系统框架提供一些基本infrastructure给所有app来运行,而你提供自己编写的代码来定制app的外观和行为.因此,了解iOS infrastructure和它们如何工作对编写app是很有帮助的. Main函数入口 所有基于C编写的app的入口都是main函数,但iOS应用程序有点不同.不同就是你不需要为iO

[渣译文] 使用 MVC 5 的 EF6 Code First 入门 系列:为ASP.NET MVC应用程序使用高级功能

这是微软官方教程Getting Started with Entity Framework 6 Code First using MVC 5 系列的翻译,这里是第十二篇:为ASP.NET MVC应用程序使用高级功能 原文:Advanced Entity Framework 6 Scenarios for an MVC 5 Web Application 译文版权所有,谢绝全文转载--但您可以在您的网站上添加到该教程的链接. 在之前的教程中,您已经实现了继承.本教程引入了当你在使用实体框架Code

ASP.NET的WEB页面的生命周期

ASP.NET的WEB页面的生命周期,从生成到销毁,也经历了不同的阶段和过程.对于ASP.NET页面生命周期中将执行一些列处理步骤.这些步骤包括初始化.实例化控件.还原和维护状态.运行时间处理程序代码及呈现.一般来说,也要经历如下各个阶段1.页请求:页请求发生在页面生命周期开始之前,用户请求页时,ASP.NET将确定是否需要分析和编译页2.开始:在开始阶段,将设置页属性,如Request和Response.在此阶段,页还将确定请求是回发请求还是新请求,并设置IsPostBack属性3.页初始化:

将最小的OWIN身份验证添加到现有的ASP.NET MVC应用程序

https://weblog.west-wind.com/posts/2015/Apr/29/Adding-minimal-OWIN-Identity-Authentication-to-an-Existing-ASPNET-MVC-Application 将最小的OWIN身份验证添加到现有的ASP.NET MVC应用程序 2015年4月29日•来自毛伊岛,HI•    40条评论 从ASP.NET 4开始,ASP.NET提供了一个相当有用的身份系统.如果您创建一个新项目并选择一个MVC项目并选