• MVC学习笔记---MVC生命周期及管道


    ASP.NET和ASP.NET MVC的HttpApplication请求处理管道有共同的部分和不同之处,本系列将体验ASP.NET MVC请求处理管道生命周期的19个关键环节。

      ①以IIS6.0为例,首先由w3wp.exe维护着一个工作进程


    1

      ②如果是第一次加载,由Aspnet_isapi.dll加载.NET运行时


    2

      ③一个工作进程里有一个应用程序池,其中可以承载多个应用程序域AppDomain


    3

      ④HTTP.SYS接收请求,通过应用程序域工厂AppDomainFactory创建应用程序域AppDomain


    4

      ⑤一个IsapiRuntime被加载,并创建一个IsapiWorkerRequest对象封装当前的HTTP请求,并把该IsapiWorkerRequest对象传递给ASP.NET的HttpRuntime运行时,此时,  HTTP请求开始进入ASP.NET请求管道


    5

    也就是说,HttpRuntime是ASP.NET请求管道的入口。当请求进来,首先进入HttpRuntime,由HttpRuntime来决定如何处理请求。默认情况下,在machine.config和Web.config中并没有显式定义httpRuntime节点,但该节点是有默认值的,如下:

    复制代码
    <httpRuntime 
       apartmentThreading="false"
       appRequestQueueLimit="5000"
       delayNotificationTimeout="5"
       enable="true"
       enableHeaderChecking="true"
       enableKernelOutputCache="true"
       enableVersionHeader="true"
       encoderType = "System.Web.Util.HttpEncoder"
       executionTimeout="110"
       maxQueryStringLength = "2048"
       maxRequestLength="4096"
       maxUrlLength = "260"
       maxWaitChangeNotification="0"
       minFreeThreads="8"
       minLocalRequestFreeThreads="4"
       relaxedUrlToFileSystemMapping = "False"
       requestLengthDiskThreshold="80"
       requestPathInvalidCharacters = "<,>,*,%,&,:,"
       requestValidationMode = "4.0"
       requestValidationType = "System.Web.Util.RequestValidator"
       requireRootedSaveAsPath="true"
       sendCacheControlHeader="true"
       shutdownTimeout="90"
       useFullyQualifiedRedirectUrl="false"
       waitChangeNotification="0" />
    复制代码

    通常,我们可以在Web.config中更改httpRuntime节点的默认值,如下:

    复制代码
    <configuration>
      <system.web>
      <httpRuntime maxRequestLength="4000"
        enable = "True"
        requestLengthDiskThreshold="512
        useFullyQualifiedRedirectUrl="True"
        executionTimeout="45"
        versionHeader="1.1.4128"/>
      </system.web>
    </configuration>
    复制代码

      ⑥HttpRuntime维护着一个HttpApplication池,当有HTTP请求过来,从池中选取可用的HttpApplication处理请求


    6

    HttpApplication有19个管道事件,分别是:

    复制代码
    1、BeginRequest:HTTP管道开始处理请求时,会触发BeginRequest事件
    2、AuthenticateRequest:安全模块对请求进行身份验证时触发该事件
    3、PostAuthenticateRequest:安全模块对请求进行身份验证后触发该事件
    4、AuthorizeRequest:安全模块对请求进程授权时触发该事件
    5、PostAuthorizeRequest:安全模块对请求进程授权后触发该事件
    6、ResolveRequestCache:缓存模块利用缓存直接对请求进程响应时触发该事件
    7、PostResolveRequestCache:缓存模块利用缓存直接对请求进程响应后触发该事件
    8、PostMapRequestHandler:对于访问不同的资源类型,ASP.NET具有不同的HttpHandler对其进程处理。对于每个请求,ASP.NET会根据扩展名选择匹配相应的HttpHandler类型,成功匹配后触发该事件
    9、AcquireRequestState:状态管理模块获取基于当前请求相应的状态(比如SessionState)时触发该事件
    10、PostAcquireRequestState:状态管理模块获取基于当前请求相应的状态(比如SessionState)后触发该事件
    11、PreRequestHandlerExecute:在实行HttpHandler前触发该事件
    12、PostRequestHandlerExecute:在实行HttpHandler后触发该事件
    13、ReleaseRequestState:状态管理模块释放基于当前请求相应的状态时触发该事件
    14、PostReleaseRequestState:状态管理模块释放基于当前请求相应的状态后触发该事件
    15、UpdateRequestCache:缓存模块将HttpHandler处理请求得到的相应保存到输出缓存时触发该事件
    16、PostUpdateRequestCache:缓存模块将HttpHandler处理请求得到的相应保存到输出缓存后触发该事件
    17、LogRequest:为当前请求进程日志记录时触发该事件
    18、PostLogReques:为当前请求进程日志记录后触发该事件
    19、EndRequest:整个请求处理完成后触发该事件
    复制代码

    我们可以在全局配置文件Global.asax中,按照约定的规则Application_{Event Name}来对管道事件定制:

      ⑦根据IsapiWorkerRequest对象,HttpRuntime创建HttpContext对象


    7

      ⑧HttpApplicationFactory创建新的或者从HttpApplication池获取现有的、可用的HttpApplication对象

    HttpApplication的工作包括:

    ● 初始化的时候加载全部的HttpModule
    ● 接收请求
    ● 在不同阶段引发不同的事件,使得HttpModule通过订阅事件的方式加入到请求的处理过程中
    ● 在一个特定阶段获取一个IHttpHandler实例,最终将请求交给具体的IHttpHandler来实现

    8

      ⑨接下来,就是HttpModules发挥作用的时候


    9


    所有的HttpModules都实现了IHttpModule接口:

    public interface IHttpModule
    {
      void Init(HttpApplication app);
      void Dispose();
    }

    可见,HttoModules正是由Init方法,根据传入的HttpApplication类型参数,订阅了HttpApplication的所有事件。

    我们自定义一个HttpModule:

    复制代码
    public class TestModule : IHttpModule
    {
      public void Dispose(){}
      public void Init(HttpApplication app)
      {
        app.PostAcquireRequestState += new EventHandler(app_PostAcuiredRequestState);
        app.PreRequestHandlerExecute += new EventHandler(app_PreRequestHandlerExecute);
      }
    
      void app_PreRequestHandlerExecute(object sender, EventArgs e)
      {
        //TODO:
      }
    
      void app_PostAcquiredRequestState(object sender, EventArgs e)
      {
        //TODO:
      }
    }
    复制代码

      ⑩当某个请求与一个规则匹配后,ASP.NET会调用匹配的HttpHandlerFactory的GetHandler方法来获取一个HttpHandler实例, 最后由一个HttpHandler实例来处理当前请求,生成响应内容


    10

    所有的HttpHandlers都实现了IHttpHandler接口:

    public interface IHttpHandler
    {
      bool IsReusable{get;}
      void ProcessRequest(HttpContext context);
    }

    比如我们可以自定义一个HttpHandler来响应一类特定的请求:

    复制代码
    public class Login : IHttpHandler
    {
      public void ProcessRequest(HttpContext context)
      {
        context.Response.ContentType = "text/plain";
        string username = context.Request.Form["name"];
        string password = context.Request.Form["password"];
    
        if(password="sth")
        {
          System.Web.Security.FormsAuthentication.SetAuthCookie(username, false);
          context.Response.Write("ok");
        }
        else
        {
          context.Response.Write("用户名和密码不正确");
        }
      }
    }
    复制代码

    ⑾ASP.NET MVC的入口在UrlRoutingModule,即订阅了HttpApplication的第7个管道事件PostResolveRequestCahce,换句话说,是在HtttpApplication的第7个管道事件处对请求进行了拦截


    11

    UrlRouteModlue实现了IHttpModule:

    复制代码
    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)
        {
            HttpContextBase context = new HttpContextWrapper(((HttpApplication) sender).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.CurrentUICulture, 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);
                    }
                }
            }
        }
     
        void IHttpModule.Dispose()
        {
            this.Dispose();
        }
     
        void IHttpModule.Init(HttpApplication application)
        {
            this.Init(application);
        }
     
        // Properties
        public RouteCollection RouteCollection
        {
            get
            {
                if (this._routeCollection == null)
                {
                    this._routeCollection = RouteTable.Routes;
                }
                return this._routeCollection;
            }
            set
            {
                this._routeCollection = value;
            }
        }
    }
    复制代码

    UrlRoutingModule是在Web.config或默认的web.config中配置:

    <httpModules>
                ......
                <add name="UrlRoutingModule-4.0" type="System.Web.Routing.UrlRoutingModule" />
            </httpModules>

      ⑿而在请求到达UrlRoutingModule之前,我们在全局文件中做了如下配置

    复制代码
    public class MvcApplication : System.Web.HttpApplication
        {
            protected void Application_Start()
            {
                ......
                BundleConfig.RegisterBundles(BundleTable.Bundles);
            }
        }
    
        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 }
                );
            }
        }
    复制代码

    这意味着:在HttpApplication的第一个管道事件BeginRequest处,通过MapRoute()方法把路由注册到了RouteCollection中。在实际使用中,UrlRoutingModule是通过RouteTable的静态属性RouteCollection获取路由。

    12

      ⒀当请求到达UrlRoutingModule的时候,UrlRoutingModule取出请求中的Controller、Action等RouteData信息,与路由表中的所有规则进行匹配,若匹配,把请求交给IRouteHandler,即MVCRouteHandler

    13

    MVCRouteHandler是用来生成实现IHttpHandler接口的MvcHandler:

    复制代码
    namespace System.Web.Routing
    {  
        public interface IRouteHandler
        {       
            IHttpHandler GetHttpHandler(RequestContext requestContext);
        }
    }
    复制代码

    UrlRoutingModule如何把请求交给MVCRouteHandler?
    通过分析UrlRoutingModule的源码可以看到:

    //通过RouteCollection的静态方法GetRouteData获取到封装路由信息的RouteData实例
    RouteData routeData = this.RouteCollection.GetRouteData(context);

    //再从RouteData中获取MVCRouteHandler
    IRouteHandler routeHandler = routeData.RouteHandler;

    为什么可以从RouteData中拿到MVCRouteHadnler呢?
    因为当我们在HttpApplication的第一个管道事件,使用MapRoute()方法注册路由的时候,已经通过Route类的构造函数把MVCRouteHandler注入到路由中了。

    复制代码
    public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces) {
                if (routes == null) {
                    throw new ArgumentNullException("routes");
                }
                if (url == null) {
                    throw new ArgumentNullException("url");
                }
    
                Route route = new Route(url, new MvcRouteHandler()) {
                    Defaults = new RouteValueDictionary(defaults),
                    Constraints = new RouteValueDictionary(constraints),
                    DataTokens = new RouteValueDictionary()
                };
    
                if ((namespaces != null) && (namespaces.Length > 0)) {
                    route.DataTokens["Namespaces"] = namespaces;
                }
    
                routes.Add(name, route);
    
                return route;
            }
    复制代码

      ⒁MVCRouteHandler把请求交给MvcHandler

    还是从UrlRoutingModule的源码可以看到,通过HttpHandler的GetHttpHandler()方法获取到了实现了IHttpHandler接口的MVCHandler:

    IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);
    context.RemapHandler(httpHandler);

    14

    MvcHandler的部分源码为:

    复制代码
    public class MvcHandler : IHttpAsyncHandler, IHttpHandler, IRequiresSessionState
    {
            protected internal virtual void ProcessRequest(HttpContextBase httpContext)
            {
                SecurityUtil.ProcessInApplicationTrust(() =>
                {
                    IController controller;
                    IControllerFactory factory;
                    ProcessRequestInit(httpContext, out controller, out factory);//初始化了ControllerFactory
                    try
                    {
                        controller.Execute(RequestContext);
                    }
                    finally
                    {
                        factory.ReleaseController(controller);
                    }
                });
            }
    
             private void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory) {
                bool? isRequestValidationEnabled = ValidationUtility.IsValidationEnabled(HttpContext.Current);
                if (isRequestValidationEnabled == true) {
                    ValidationUtility.EnableDynamicValidation(HttpContext.Current);
                }
                AddVersionHeader(httpContext);
                RemoveOptionalRoutingParameters();
                string controllerName = RequestContext.RouteData.GetRequiredString("controller");
                factory = ControllerBuilder.GetControllerFactory();
                controller = factory.CreateController(RequestContext, controllerName);
                if (controller == null) {
                  throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture,MvcResources.ControllerBuilder_FactoryReturnedNull,factory.GetType(),controllerName));
                }
            }
    }
    复制代码

      ⒂从以上可以看出:首先通过ControllerBuilder的静态方法GetControllerFactory获取到实现IControllerFactory接口的ControllerFactory,然后根据从上下文中的路由数据中拿到controller名称,并据此创建实现IController接口的Controller

    15

    Controller派生于ControllerBase, 而ControllerBase实现了IController接口。ControllerBase的部分源码如下:

    复制代码
    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,都会执行基类ControllerBase的Execute()方法
    ● Execute()方法又会调用ExecuteCore()这个抽象方法
    ● ExecuteCore()这个抽象方法的实现被定义在Controller中
    ● 在Controller中的ExecuteCore()方法会调用ActionInvoker的InvokeAction()方法

      ⒃ActionInvoker激发Action方法

    16

    ActionInvoker实现了IActionInvoker接口:

    public interface IActionInvoker
    {
      bool InvokeAction(ControllerContext controllerContext, string actionName);
    }

    MVC默认的ActionInvoker是ControllerActionInvoker。

    在Controller类中,提供了类型为IActionInvoker的属性ActionInvoker,当执行ExecuteCore()方法时会让这个ActionInvoker调用InvokeAction()方法激发Action。如下:

    复制代码
    public class Controller
    {
      ......
      private IActionInvoker _actionInvoker;
      public IActionInvoker ActionInvoker
      {
        get
        {
          if(_actionInvoker == null)
          {
            _actionInvoker = CreateActionInvoker();
          }
          return _actionInvoker;
        }
        set
        {
          _actionInvoker = value;
        }
      }
    
      protected virtual IActionInvoker CreateActionInvoker()
      {
        return new ControllerActionInvoker();
      }
    
       public override void ExecuteCore()
       {
         ActionInvoker.InvokeAction(...);
       }
       .....
    }
    复制代码


    ActionInvoker在执行InvokeAction()方法时会需要有关Controller和Action的相关信息,实际上,Controller信息(比如Controller的名称、类型、包含的Action等)被封装在ControllerDescriptor这个类中,Action信息(比如Action的名称、参数、属性、过滤器等)被封装在ActionDescriptor中。

    另外,ActionDescriptor还提供了一个FindAction()方法,用来找到需要被执行的Action。

      ⒄ActionInvoker在执行InvokeAction()方法返回ActionResult

    17

    ActionResult是一个抽象类:

    public abstract class ActionResult
    {
      public abstract void ExecuteResult(ControllerContext context);
    }

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

      ⒅ViewEngine找到需要被渲染的视图

    18

    默认的有Razor View Engine和Web Form View Engine,实现IViewEngine接口。

    IViewEngine接口方法:
    ● FindPartialView
    ● FindView
    ● ReleaseView

    如果要创建自定义View Engine,只需要派生于VirtualPathProviderViewEngine这个类。

      ⒆View被加载成WebViewPage<TModel>类型,并渲染生成Html

    19

    调用ViewResult的ExecuteResult()方法,通过IView的Render()方法渲染成Html。

    复制代码
    public abstract class ViewResultBase : ActionResult
    {
            public override void ExecuteResult(ControllerContext context)
            {
                if (context == null)
                {
                    throw new ArgumentNullException("context");
                }
                if (String.IsNullOrEmpty(ViewName))
                {
                    ViewName = context.RouteData.GetRequiredString("action");
                }
    
                ViewEngineResult result = null;
    
                if (View == null)
                {
                    //通过视图引擎获取到ViewEngineResult ,此时模板页面【aspx】被加载成了WebViewPage<TModel>
                    result = FindView(context);
                    View = result.View;
                }
    
                TextWriter writer = context.HttpContext.Response.Output;
                ViewContext viewContext = new ViewContext(context, View, ViewData, TempData, writer);
                View.Render(viewContext, writer);
    
                if (result != null)
                {
                    result.ViewEngine.ReleaseView(context, View);
                }
            }
    }
    复制代码
    复制代码
    void Application_Start(object sender, EventArgs e) {}
    void Application_End(object sender, EventArgs e) {}
    void Application_Error(object sender, EventArgs e) {}
    void Session_Start(object sender, EventArgs e) {}
    void Session_End(object sender, EventArgs e) {}
    ......
  • 相关阅读:
    Spark Streaming 调优指南
    Hive实战之Youtube数据集
    【源码解析】BlockManager详解
    Spark操作HBase问题:java.io.IOException: Non-increasing Bloom keys
    Spark实战之读写HBase
    ZooKeeper的简单理解
    Flume-ng源码解析之Source组件
    Flume-ng源码解析之Sink组件
    Flume-ng源码解析之Channel组件
    Flume-ng源码解析之启动流程
  • 原文地址:https://www.cnblogs.com/changrulin/p/4769353.html
Copyright © 2020-2023  润新知