zoukankan      html  css  js  c++  java
  • Asp.Net MVC3 简单入门第一季(四)详解Request Processing Pipeline

    引子

          很久没更新了,今天写点关于Asp.Net MVC的PipeLine。首先我们确认一点,Asp.Net WebFrom和Asp.Net MVC是在.Net平台下的两种web开发方式。其实他们都是基于Asp.Net Core的不同表现而已。看下面一张图,我们就能理解了WebForm和Asp.Net MVC的一个关系了。

    那好我们了解了Asp.Net平台下的两种开发方式,相信大家对于WebForm的Pipeline都非常熟悉了,当然这也是你熟悉Asp.Net开发的必经之路。而看了很多关于Asp.Net MVC的资料很少有把整个Pipeline讲的非常清楚的。我暂时将自己浅陋的整理和理解总结如下,欢迎高手拍砖!

    第一阶段:客户端请求

    客户端通过浏览器、其他软件、自己编写WebClinet、模拟HttpRequest等方法来请求一个URL。当然在Asp.Net WebFrom下,所有的请求都是归结到Handler上,普通的Aspx、Ascx等都是继承自IHttpHandler接口的一些实例,所以我总结出来:WebFrom下所有的请求都是请求的Handler【不考虑Url重写】。而做Asp.Net MVC的项目呢,所有的请求是都归结到Action上,Url应该是直接请求Action。

    客户端发出请求后,此请求就会通过网络发出,可能经过多个路由、还可能经过域名解析等等....

    可能请求的是一个集群IP或者单个服务器,但是最终肯定只能由一台Web服务器的来处理此次请求。

    第二阶段:IIS Web服务器

            当一个请求到达IIS服务器后,Windows系统的内核模块 HTTP.SYS就能监听到此次请求,并将此次请求的URL、IP以及端口等信息解析出来并将此请求交给注册的应用来处理:也就是IIS的站点。请求此时就到达了IIS,IIS【此处仅代表IIS6.0版本】就会去检查此次请求的URL的后缀并将相应的请求交给配置的处理后缀相应的isapi。如果是.aspx或者ascx等直接交给默认设置了此处理项的AspNet_isapi.dll来处理,如果我们想处理Asp.Net MVC的请求的话,我们需要在IIS里面设置处理*.*请求交给AspNet_isapi.dll来处理,才能将一个普通的MVC请求的URL:Http://localhost/DemoController/DemoAction交给AspNet_Isapi.dll来处理。

    第三阶段:Asp.Net 运行时

    此时请求到AspNet_Isapi.dll后,它负责启动Asp.Net RunTime【如过启动了,直接将请求交给RunTime】。Asp.Net 运行时【HttpRuntime】此时会初始化一下HttpContext上下文,并从HttpApplicationFactory去创建一个HttpApplication对象,并将HttpContext赋值给HttpApplication,此后HttpContext的信息就会一直在管道内往下传递。

    HttpApplication对象开始初始化WebConfig文件中注册的IHttpModule,请求带着请求信息【HttpContext】随着管道流过多个HttpModule【一般可以做为权限校验、行为记录、日志等等,就是在到达Handler之前我们都可以直接处理此次Http请求,甚至可以重写URL】,当然也会经过我们注册的一些自定义的IHttpModule,在.Net 4.0的machine  的config文件中默认配置了一个URLRouteModule,这个也就是我们普通的Asp.Net MVC项目中的路由DLL引用【System.Web.Routing】内部的一个实现了IHttpModule接口的实例类。请求最终流向了路由组件。

    第四阶段:Routing组件

    如果你用的是MVC 2+ .NET 3.5,则你会在你的web项目中发现UrlRoutingModule就配置在你的Web.Config。.NET 4却是在.Net的默认配置文件中配置的。

    UrlRoutingModule做了这么几个工作:首先他会拿着你的请求到路由表中去匹配相应的路由规则。而路由表规则的定义是在HttpApplication初始化的时候由静态方法执行的,且看一个普通的Asp.Net MVC项目的Global.asax

     public class MvcApplication : System.Web.HttpApplication
    {
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
    filters.Add(new HandleErrorAttribute());
    }
    public static void RegisterRoutes(RouteCollection routes)//定义路由表规则
    {
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
    routes.MapRoute(
    "Default", // 路由名称
    "{controller}/{action}/{id}", // 带有参数的 URL
    new { controller = "Home", action = "Index", id = UrlParameter.Optional } // 参数默认值
    );
    }
    protected void Application_Start()
    {
    AreaRegistration.RegisterAllAreas();
    RegisterGlobalFilters(GlobalFilters.Filters);
    RegisterRoutes(RouteTable.Routes);//注册路由表
    }
    }

    而路由表的规则的注册是在 Application_Start() 方法内部,那此时请求在URLRouteModule内部到路由表中的所有规则进行匹配,并把匹配的Controller的信息和Action的信息以及RouteData等信息都解析处理,然后将请求进一步交给:实现了IRouteHandler【实现了IHttpHandler接口】 的一个实例,下面是IRouteHandler的源码:

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

    如果你想自己来实现这个接口然后在Web.Config中配置一下,那么请求就到了你自己的自定义的RouteHandler来执行后续的请求处理操作了。如果你使用的是默认的配置,那么请求会传递到MvcRouteHandler,那么请求f附加着HttpContext就会到达Asp.Net MVC的处理中了。

    第五阶段:MvcRouteHandler创建Controller

    请求到此,其实跟WebForm都是一致的,而后面才出现了一些不同,此时请求才真正的进入System.Web.Mvc控制的领域内。后面所有的东西我们都可以直接通过源码来介绍了,而上面的所有的请求处理只能通过反射等方式来看或者学习,而后面的内容,我们可以幸福的直接看源码了。那就跟我走进它的管道怎么流动的吧...

    接着上面讲,请求到了MvcRouteHandler类,而此类的源码如下:

    namespace System.Web.Mvc
    {
    using System.Web.Routing;
    using System.Web.SessionState;
    public class MvcRouteHandler : IRouteHandler
    {
    private IControllerFactory _controllerFactory;
    public MvcRouteHandler()
    {
    }
    public MvcRouteHandler(IControllerFactory controllerFactory)
    {
    _controllerFactory = controllerFactory;
    }
    protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext)//实现了IRouteHandler的方法,URLRouteModule调用
    {
    requestContext.HttpContext.SetSessionStateBehavior(GetSessionStateBehavior(requestContext));
    return new MvcHandler(requestContext);
    }
    .....
    }

    MvcRouteHandler的GetHttpHandler方法被URLRouteModule调用,而看上面的红色源码部分我们看到,它将请求上下文交给了MVCHandler,并返回了MVCHandler。

    而我查看源码得知:MVCHandler实现了IHttpHandler,此时它的ProcessRequest方法被调用。且看MVCHandler的部分源代码:

     public class MvcHandler : IHttpAsyncHandler, IHttpHandler, IRequiresSessionState
    {
    protected internal virtual void ProcessRequest(HttpContextBase httpContext)
    {
    SecurityUtil.ProcessInApplicationTrust(() =>
    {
    IController controller;//在ProcessRequestInit方法中:controller = factory.CreateController(RequestContext, controllerName);//初始化
                    IControllerFactory factory;//是由ProcessRequestInit方法中这行代码初始化的: factory = ControllerBuilder.GetControllerFactory();
                    ProcessRequestInit(httpContext, out controller, out factory);//初始化了ControllerFactory
    try
    {
    controller.Execute(RequestContext);
    }
    finally
    {
    factory.ReleaseController(controller);
    }
    });
    }
    }

    从源码中我们得知:请求交给MVCHandler后,它首先从ControllerBuilder获取到当前的实现了IControllerFactory接口的ControllerFactory【也可以自己定义相关的CustomerControllerFactory,然后在Glable中注册使用】。然后根据上下文中请求的Controller的字符串信息创建出实现了IController接口的控制器。然后调用了上面代码中红色部分,也就是controller.Execute(RequestContext);

    那此时请求就交给了controller。

    第六阶段:Controller调用Action返回ActionResult

    由于此文过长,而且时间已经到了凌晨。源码我就不贴了,简单介绍一下流程,后面再做详细赘述。

    Controller的Execute方法是在基类ControllerBase中的方法,而此方法又调用ExecuteCore方法,然后此方法内部执行如下代码:

    string actionName = RouteData.GetRequiredString("action");
    if (!ActionInvoker.InvokeAction(ControllerContext, actionName))
    {
    HandleUnknownAction(actionName);
    }

    首先从RouteData中获取Action的名字,然后调用ActonInvoker的InvokeAction方法,调用Action执行。Action的返回的ActionResult的ExecuteResult(controllerContext)方法被执行,那此时就出现了分叉。如果直接返回的非ViewResult的话,那就直接协会到Respose流了返回客户端了,如果是ViewResult的话,那就进入View的领域了。

    第七阶段:View视图加载成Page类,并Render成Html

    此时请求到ViewResult后,ExecuteResult方法被调用,且看此方法的内部实现:

     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)
    {
    result = FindView(context);//通过视图引擎获取到ViewEngineResult ,此时模板页面【aspx】被加载成了对应的ViewPage类
    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);
    }
    }

           内部主要是通过ViewResult的FindView方法通过ViewEngine去加载具体的Aspx页面或者是cshtml页面生成对应的page类【针对Aspx】,然后再调用IView接口的Render方法将请求信息+ViewData的信息以等一块渲染成Html并写回到客户端。

    在此阶段我们发现IViewEngine内部的实现这是到规定路径下去加载Aspx页面生成对应的ViewPage类。

    IView接口的Render方法才是真正的去将Html和数据装配的到一块。

    自此请求结束。

    总结:

    客户端请求→路由器→IIS服务器内核模块HTTP.SYS→IIS→AspNet_isapi.dll→Asp.Net Runtime→Application→IHttpModule....IHttpModule→MVCRouteModule→MVCRouteHandler→MVCHandler→ControllerFactory

    →Controller→ActionInvoke→Aciton→ActiongResult.ExcuteReuslt()【如果是ViewResult】→IViewEngine FindView→IView Render→Response

    最后附两张关于此请求管道的两张图,以飨读者。

    记于 2011年10月12日0:24:42
    转载请注明出处:博客园Flydragonhttp://www.cnblogs.com/fly_dragon

    初识Asp.Net MVC2.0

    初识Asp.Net MVC2.0【续】

    Asp.Net MVC2.0 Url 路由入门---实例篇

    Asp.Net MVC2.0 Url 路由入门

    Asp.Net MVC3 简单入门第一季(一)环境准备

    Asp.Net MVC3 简单入门第一季(二)详解Asp.Net MVC3项目

    Asp.Net MVC3 简单入门第一季(三)详解Controller之FilteAsp.Net MVC3 简单入门第一季(四)详解Request Processing Pipeline

     

    作者:FlyDragon

    出处:http://www.cnblogs.com/fly_dragon/

    关于作者:专注于微软平台项目架构、管理和企业解决方案。如有问题或建议,请多多赐教!

    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,如有问题,可以通malun666@126.com 联系我,非常感谢。

  • 相关阅读:
    TypeScript完全解读(26课时)_2.TypeScript完全解读-基础类型
    Flutter实战视频-移动电商-48.详细页_详情和评论的切换
    Flutter实战视频-移动电商-47.详细页_Flutter_html插件的使用
    TypeScript完全解读(26课时)_1.TypeScript完全解读-开发环境搭建
    [Android] The connection to adb is down, and a severe error has occured
    每日一小练——求质数
    C++语言笔记系列之十八——虚函数(1)
    Android 输入管理服务-输入事件向详细应用的分发
    Android技术归档
    C++编写绚丽的界面
  • 原文地址:https://www.cnblogs.com/fly_dragon/p/AspNetMVC_Pipeline.html
Copyright © 2011-2022 走看看