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

简介:

  首先我们知道http是一种无状态的请求,他的生命周期就是从客户端浏览器发出请求开始,到得到响应结束。那么MVC应用程序从发出请求到获得响应,都做了些什么呢?

      本文我们会详细讨论MVC应用程序一个请求的生命周期,从一个控件到另一个控件是怎样被处理的。我们还会详细介绍一下整个请求的生命周期中,用到的相关组件。因为在平常的开发过程中,我们可能知道怎样去使用MVC框架来处理相关的请求,大部分的时候我们只是在controller和action方法之间做相关的处理,对于真正内在的运行机制可能不是很了解。其实当我们对内在机制有了一定的了解以后,会发现微软的MVC框架的扩展性很强,到处都留有扩展接口,让我们通过扩展能够自己定义自己所需要的处理机制,这也正是为什么MVC框架如此出名的原因。

当我最开始学习使用mvc的时候,困扰我的一个问题就是,一个请求的流程控制是怎样的呢?从view到controller再到action之间经历了什么?那个时候我还不清楚HTTP module和HTTP  handler在处理一个请求中扮演什么样的角色,起什么样的作用呢。毕竟MVC是一个web开发框架,在整个请求处理过程中,肯定包含了http module和http handler。其实还有很多相关的组件包含在一个完整的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说起。先看看微软官方是怎么定义HttpApplication的:

定义 ASP.NET 应用程序中的所有应用程序对象共有的方法、属性和事件。此类是用户在 Global.asax 文件中所定义的应用程序的基类。

可能我翻译不是很准确,原文连接在这里:https://msdn.microsoft.com/en-us/library/system.web.httpapplication(v=vs.110).aspx

微软官方文档中Remark里有这么一段话:HttpApplication 类的实例是在 ASP.NET 基础结构中创建的,而不是由用户直接创建的。使用HttpApplication 类的一个实例来处理其生存期中收到的众多请求。但是,它每次只能处理一个请求。这样,成员变量才可用于存储针对每个请求的数据。

意思就是说ASP.NET应用程序,不管是MVC还是WebForm,最终都会到达一个HttpApplication类的实例。HttpApplication是整个ASP.NET基础架构的核心,负责处理分发给他的请求。HttpApplication处理请求的周期是一个复杂的过程,在整个过程中,不同阶段会触发相映的事件。我们可以注册相应的事件,将处理逻辑注入到HttpApplication处理请求的某个阶段。在HttpApplication这个类中定义了19个事件来处理到达HttpApplication实例的请求。就是说不管MVC还是WebForm,最终都要经过这19个事件的处理,那么除了刚才说的MVC和WebFrom在请求处理机制上大部分都是相同的,不同之处在哪呢?他们是从哪里开始分道扬镳的呢?我们猜想肯定就在这19个方法中。我们继续往下看。

我们来看看这19个事件:

应用程序按照以下顺序执行由 global.asax 文件中定义的模块或用户代码处理的事件:

事件名称:

简单描述:

BeginRequest

在 ASP.NET 响应请求时作为 HTTP 执行管线链中的第一个事件发生

AuthenticateRequest

当安全模块已建立用户标识时发生。注:AuthenticateRequest事件发出信号表示配置的身份验证机制已对当前请求进行了身份验证。预订 AuthenticateRequest 事件可确保在处理附加的模块或事件处理程序之前对请求进行身份验证

PostAuthenticateRequest

当安全模块已建立用户标识时发生。PostAuthenticateRequest事件在 AuthenticateRequest 事件发生之后引发。预订PostAuthenticateRequest 事件的功能可以访问由PostAuthenticateRequest 处理的任何数据

AuthorizeRequest

当安全模块已验证用户授权时发生。AuthorizeRequest 事件发出信号表示 ASP.NET 已对当前请求进行了授权。预订AuthorizeRequest 事件可确保在处理附加的模块或事件处理程序之前对请求进行身份验证和授权

PostAuthorizeRequest

在当前请求的用户已获授权时发生。PostAuthorizeRequest 事件发出信号表示 ASP.NET 已对当前请求进行了授权。预订PostAuthorizeRequest 事件可确保在处理附加的模块或处理程序之前对请求进行身份验证和授权

ResolveRequestCache

当 ASP.NET 完成授权事件以使缓存模块从缓存中为请求提供服务时发生,从而跳过事件处理程序(例如某个页或 XML Web services)的执行

PostResolveRequestCache

在 ASP.NET 跳过当前事件处理程序的执行并允许缓存模块满足来自缓存的请求时发生。)在 PostResolveRequestCache 事件之后、PostMapRequestHandler 事件之前创建一个事件处理程序(对应于请求 URL 的页

PostMapRequestHandler

在 ASP.NET 已将当前请求映射到相应的事件处理程序时发生。

AcquireRequestState

当 ASP.NET 获取与当前请求关联的当前状态(如会话状态)时发生。

PostAcquireRequestState

在已获得与当前请求关联的请求状态(例如会话状态)时发生。

PreRequestHandlerExecute

恰好在 ASP.NET 开始执行事件处理程序(例如,某页或某个 XML Web services)前发生。

PostRequestHandlerExecute

在 ASP.NET 事件处理程序(例如,某页或某个 XML Web service)执行完毕时发生。

ReleaseRequestState

在 ASP.NET 执行完所有请求事件处理程序后发生。该事件将使状态模块保存当前状态数据。

PostReleaseRequestState

在 ASP.NET 已完成所有请求事件处理程序的执行并且请求状态数据已存储时发生。

UpdateRequestCache

当 ASP.NET 执行完事件处理程序以使缓存模块存储将用于从缓存为后续请求提供服务的响应时发生。

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 执行管线链中的最后一个事件发生。

在调用 CompleteRequest 方法时始终引发 EndRequest 事件。

对于一个ASP.NET应用程序来说,HttpApplication派生与Global.aspx(可以看看我们创建的应用程序都有一个Global.aspx文件),我们可以在Global.aspx文件中对HttpApplication的请求进行定制即注入这19个事件中的某个事件进行逻辑处理操作。在Global.aspx中我们按照"Application_{Event Name}"这样的方法命名进行事件注册。

Event Name就是上面19个事件的名称。比如Application_EndRequest就用于处理Application的EndRequest事件。

HttpModule

ASP.NET拥有一个高度可扩展的引擎,并且能够处理对于不同资源类型的请求。这就是HttpModule。当一个请求转入ASP.net管道时,最终负责处理请求的是与资源相匹配的HttpHandler对象,但是在HttpHandler进行处理之前,ASP.NET先会加载并初始化所有配置的HttpModule对象。HttpModule初始化的时候,会将一些回调事件注入到HttpApplication相应的事件中。所有的HttpModule都实现了IHttpModule接口,该接口有一个有一个Init方法。

复制代码

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的主要经历的管道事件:

上图就是一个完整的mvc应用程序的一个http请求到响应的整个儿所经历的流程。从UrlRoutingModule拦截请求到最终ActionResult执行ExecuteResult方法生成响应。

下面我们就来详细讲解一下这些过程都做了些什么。

UrlRoutingModule                                                

MVC应用程序的入口UrlRoutingModule

首先发起一个请求,我们前面讲到ASP.NET 会加载一个HttpModule对象的初始化事件Init,而所有的HttpModule对象都实现了IHttpModule接口。我们看看UrlRoutingModule的实现:

从上图中我们看到UrlRoutingModule实现了接口IHttpModule,当一个请求转入ASP.NET管道时,就会加载 UrlRoutingModule对象的Init()方法。

那么为什么偏偏是UrlRoutingModule被加载初始化了呢?为什么不是别的HttpModule对象呢?带着这个疑问我们继续。

在ASP.NET MVC中,最核心的当属“路由系统”,而路由系统的核心则源于一个强大的System.Web.Routing.dll组件。System.Web.Routing.dll 不是MVC所特有的,但是MVC框架和它是密不可分的。

首先,我们要了解一下UrlRoutingModule是如何起作用的。

(1)IIS网站的配置可以分为两个块:全局 Web.config 和本站 Web.config。Asp.Net Routing属于全局性的,所以它配置在全局Web.Config 中,我们可以在如下路径中找到:“C\Windows\Microsoft.NET\Framework\版本号\Config\Web.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>

复制代码

大家看到没有,我上面标红的那一行:<add name="UrlRoutingModule-4.0" type="System.Web.Routing.UrlRoutingModule" />

UrlRoutingModule并不是MVC特有的,这是一个全局配置,就是说所有的ASP.NET请求都会到达这里,所以该Module还不能最终决定是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 += new EventHandler(this.OnApplicationPostResolveRequestCache);

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

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

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

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

复制代码

 1 public virtual void PostResolveRequestCache(HttpContextBase context)
 2 {
 3     // 通过RouteCollection的静态方法GetRouteData获取到封装路由信息的RouteData实例
 4     RouteData routeData = this.RouteCollection.GetRouteData(context);
 5     if (routeData != null)
 6     {
 7         // 再从RouteData中获取MVCRouteHandler
 8         IRouteHandler routeHandler = routeData.RouteHandler;
 9         ......
10         if (!(routeHandler is StopRoutingHandler))
11         {
12             ......
13             // 调用 IRouteHandler.GetHttpHandler(),获取的IHttpHandler 类型实例,它是由 IRouteHandler.GetHttpHandler获取的,这个得去MVC的源码里看
14             IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);
15             ......
16             // 合适条件下,把之前将获取的IHttpHandler 类型实例 映射到IIS HTTP处理管道中
17             context.RemapHandler(httpHandler);
18         }
19     }
20 }

复制代码

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

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

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

复制代码

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Web;
 5 using System.Web.Mvc;
 6 using System.Web.Routing;
 7 
 8 namespace ApiDemo
 9 {
10     public class RouteConfig
11     {
12         public static void RegisterRoutes(RouteCollection routes)
13         {
14             routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
15 
16             routes.MapRoute(
17                 name: "Default",
18                 url: "{controller}/{action}/{id}",
19                 defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
20             );
21         }
22     }
23 }

复制代码

我们在这个类里面,主要是给路由表添加路由规则。在看看上面的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源码就知道咯:

复制代码

 1 public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces) 
 2         {
 3             ......
 4 
 5             Route route = new Route(url, new MvcRouteHandler())             {
 6                 Defaults = new RouteValueDictionary(defaults),
 7                 Constraints = new RouteValueDictionary(constraints),
 8                 DataTokens = new RouteValueDictionary()
 9             };
10             ......
11             return route;
12         }

复制代码

看看我们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的呢:

复制代码

 1 public class MvcRouteHandler : IRouteHandler
 2 {
 3     // Fields
 4     private IControllerFactory _controllerFactory;
 5 
 6     // Methods
 7     public MvcRouteHandler()
 8     {
 9     }
10 
11     public MvcRouteHandler(IControllerFactory controllerFactory)
12     {
13         this._controllerFactory = controllerFactory;
14     }
15 
16     protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext)
17     {
18         requestContext.HttpContext.SetSessionStateBehavior(this.GetSessionStateBehavior(requestContext));
19         return new MvcHandler(requestContext);
20     }
21 
22     protected virtual SessionStateBehavior GetSessionStateBehavior(RequestContext requestContext)
23     {
24         string str = (string) requestContext.RouteData.Values["controller"];
25         if (string.IsNullOrWhiteSpace(str))
26         {
27             throw new InvalidOperationException(MvcResources.MvcRouteHandler_RouteValuesHasNoController);
28         }
29         IControllerFactory factory = this._controllerFactory ?? ControllerBuilder.Current.GetControllerFactory();
30         return factory.GetControllerSessionBehavior(requestContext, str);
31     }
32 
33     IHttpHandler IRouteHandler.GetHttpHandler(RequestContext requestContext)
34     {
35         return this.GetHttpHandler(requestContext);
36     }
37 }
38 
39  

复制代码

看第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()定义如下:

复制代码

 1 // Copyright (c) Microsoft Open Technologies, Inc.<pre>// All rights reserved. See License.txt in the project root for license information.
 2 void IHttpHandler.ProcessRequest(HttpContext httpContext) 
 3 {
 4     ProcessRequest(httpContext);
 5 }
 6 protected virtual void ProcessRequest(HttpContext httpContext) 
 7 {
 8     HttpContextBase iHttpContext = new HttpContextWrapper(httpContext);
 9                 ProcessRequest(iHttpContext);
10 }
11 protected internal virtual void ProcessRequest(HttpContextBase httpContext) {
12     SecurityUtil.ProcessInApplicationTrust(() => {
13         IController controller;
14         IControllerFactory factory;
15         ProcessRequestInit(httpContext, out controller, out factory);
16     try
17     {
18         controller.Execute(RequestContext);
19     }
20     finally
21     {
22         factory.ReleaseController(controller);
23     }
24     });
25 }

复制代码

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

复制代码

 1 private void ProcessRequestInit(HttpContextBase httpContext, 
 2              out IController controller, out IControllerFactory factory) {
 3     // If request validation has already been enabled, make it lazy.
 4     // This allows attributes like [HttpPost] (which looks
 5     // at Request.Form) to work correctly without triggering full validation.
 6     bool? isRequestValidationEnabled = 
 7        ValidationUtility.IsValidationEnabled(HttpContext.Current);
 8     if (isRequestValidationEnabled == true) {
 9         ValidationUtility.EnableDynamicValidation(HttpContext.Current);
10     }
11     AddVersionHeader(httpContext);
12     RemoveOptionalRoutingParameters();
13     // Get the controller type
14     string controllerName = RequestContext.RouteData.GetRequiredString("controller");
15     // Instantiate the controller and call Execute
16     factory = ControllerBuilder.GetControllerFactory();
17     controller = factory.CreateController(RequestContext, controllerName);
18     if (controller == null) {
19         throw new InvalidOperationException(
20         String.Format(
21             CultureInfo.CurrentCulture,
22             MvcResources.ControllerBuilder_FactoryReturnedNull,
23             factory.GetType(),
24             controllerName));
25     }
26 }

复制代码

在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接口即可。

复制代码

 1 public abstract class ControllerBase : IController
 2 {
 3     protected virtual void Execute(RequestContext requestContext)
 4     {
 5         if (requestContext == null)
 6         {
 7             throw new ArgumentNullException("requestContext");
 8         }
 9         if (requestContext.HttpContext == null)
10         {
11             throw new ArgumentException(
12               MvcResources.ControllerBase_CannotExecuteWithNullHttpContext, 
13               "requestContext");
14         }
15         VerifyExecuteCalledOnce();
16         Initialize(requestContext);
17         using (ScopeStorage.CreateTransientScope())
18         {
19             ExecuteCore();
20         }
21     }
22     protected abstract void ExecuteCore(); 
23  // ....... 

复制代码

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输出响应。

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


本文转自BloodyAngel博客园博客,原文链接:http://www.cnblogs.com/zgynhqf/p/5679032.html,如需转载请自行联系原作者

相关文章
|
3月前
|
存储 Shell Linux
快速上手基于 BaGet 的脚本自动化构建 .net 应用打包
本文介绍了如何使用脚本自动化构建 `.net` 应用的 `nuget` 包并推送到指定服务仓库。首先概述了 `BaGet`——一个开源、轻量级且高性能的 `NuGet` 服务器,支持多种存储后端及配置选项。接着详细描述了 `BaGet` 的安装、配置及使用方法,并提供了 `PowerShell` 和 `Bash` 脚本实例,用于自动化推送 `.nupkg` 文件。最后总结了 `BaGet` 的优势及其在实际部署中的便捷性。
153 10
|
26天前
|
开发框架 监控 .NET
【Azure App Service】部署在App Service上的.NET应用内存消耗不能超过2GB的情况分析
x64 dotnet runtime is not installed on the app service by default. Since we had the app service running in x64, it was proxying the request to a 32 bit dotnet process which was throwing an OutOfMemoryException with requests >100MB. It worked on the IaaS servers because we had the x64 runtime install
|
1月前
|
JSON 算法 安全
JWT Bearer 认证在 .NET Core 中的应用
【10月更文挑战第30天】JWT(JSON Web Token)是一种开放标准,用于在各方之间安全传输信息。它由头部、载荷和签名三部分组成,用于在用户和服务器之间传递声明。JWT Bearer 认证是一种基于令牌的认证方式,客户端在请求头中包含 JWT 令牌,服务器验证令牌的有效性后授权用户访问资源。在 .NET Core 中,通过安装 `Microsoft.AspNetCore.Authentication.JwtBearer` 包并配置认证服务,可以实现 JWT Bearer 认证。具体步骤包括安装 NuGet 包、配置认证服务、启用认证中间件、生成 JWT 令牌以及在控制器中使用认证信息
|
2月前
|
开发框架 .NET API
Windows Forms应用程序中集成一个ASP.NET API服务
Windows Forms应用程序中集成一个ASP.NET API服务
98 9
|
3月前
|
数据采集 JSON API
.NET 3.5 中 HttpWebRequest 的核心用法及应用
【9月更文挑战第7天】在.NET 3.5环境下,HttpWebRequest 类是处理HTTP请求的一个核心组件,它封装了HTTP协议的细节,使得开发者可以方便地发送HTTP请求并接收响应。本文将详细介绍HttpWebRequest的核心用法及其实战应用。
140 6
|
2月前
|
XML 存储 安全
C#开发的程序如何良好的防止反编译被破解?ConfuserEx .NET混淆工具使用介绍
C#开发的程序如何良好的防止反编译被破解?ConfuserEx .NET混淆工具使用介绍
76 0
|
3月前
|
Ubuntu 持续交付 API
如何使用 dotnet pack 打包 .NET 跨平台程序集?
`dotnet pack` 是 .NET Core 的 NuGet 包打包工具,用于将代码打包成 NuGet 包。通过命令 `dotnet pack` 可生成 `.nupkg` 文件。使用 `--include-symbols` 和 `--include-source` 选项可分别创建包含调试符号和源文件的包。默认情况下,`dotnet pack` 会先构建项目,可通过 `--no-build` 跳过构建。此外,还可以使用 `--output` 指定输出目录、`-c` 设置配置等。示例展示了创建类库项目并打包的过程。更多详情及命令选项,请参考官方文档。
225 11
|
3月前
|
开发框架 前端开发 JavaScript
ASP.NET MVC 教程
ASP.NET 是一个使用 HTML、CSS、JavaScript 和服务器脚本创建网页和网站的开发框架。
44 7
|
3月前
|
存储 运维
.NET开发必备技巧:使用Visual Studio分析.NET Dump,快速查找程序内存泄漏问题!
.NET开发必备技巧:使用Visual Studio分析.NET Dump,快速查找程序内存泄漏问题!
|
3月前
|
自然语言处理 C# 图形学
使用dnSpyEx对.NET Core程序集进行反编译、编辑和调试
使用dnSpyEx对.NET Core程序集进行反编译、编辑和调试