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);//初始化了ControllerFactorytry                {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

 

时间: 2024-09-14 14:02:42

Asp.Net MVC3 简单入门第一季(四)详解Request Processing Pipeline的相关文章

Asp.Net MVC3 简单入门第一季 【索引贴】

初识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之Filter Asp.Net MVC3 简单入门第一季(四)详解Request Processing Pipe

Asp.Net MVC3 简单入门第一季(五) 通过Asp.Net MVC的区域功能实现将多个MVC项目部署到一个站点

引子 本文将主要演示怎么将多个Asp.Net MVC项目部署到一个IIS站点中的例子,主要使用的是Asp.Net MVC提供的区域的功能. Asp.Net MVC提供了区域的功能,可以很方便的为大型的网站划分区域.可以让我们的项目不至于太复杂而导致管理混乱,有了区域后,每个模块的页面都放入相应的区域内进行管理很方便.而随着项目的复杂,每个开发人员开发的模块呢也可能是一个完整的解决方案,而他要开发的UI项目呢只是主站点项目的一个区域,而如果把所有的UI项目放到一个UI项目,在团队开发时就不很方便了

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

前言 大家好,从今天开始我将写一个关于AspNet MVC3方面学习的总结,并跟初学者一起分享一些基本的基础知识,作者本身也很愿意跟大家一起交流技术,一起交流一起进步,欢迎高手不吝赐教,欢迎大家不同的意见和建议,作者的学识和见识当然有自己的局限性,希望自己能成为不闷骚型的技术人员,而不是只自己享受技术,而不让更多的人来分享你的成果的人. 第一节:关于Asp.Net MVC3 Asp.Net MVC已经到第三版了,相信大家也都熟悉了,我也不再重复相关概念性的东西了.但是大家一定要了解,Asp.Ne

Asp.Net MVC3 简单入门第一季(三)详解Controller之Filter

前言 前面两篇写的比较简单,刚开始写这个系列的时候我面向的对象是刚开始接触Asp.Net MVC的朋友,所以写的尽量简单.所以写的没多少技术含量.把这些技术总结出来,然后一简单的方式让更多的人很好的接受这是我一直努力的方向.后面会有稍微复杂点的项目!让我们一起期待吧! 此文我将跟大家介绍一下Asp.Net MVC3 Filter的一些用法.你会了解和学习到全局Fileter,Action Filter等常用用法. 第一节:Filter知识储备 项目大一点总会有相关的AOP面向切面的组件,而MVC

慕课网“JAVA入门第一季”课程受偏爱 超5万人学习

作为国内最大的IT技能学习平台--慕课网,2014年12月4日再次创造业内一项行业奇迹.2014年6月9日上线的"JAVA入门第一季"课程经过短短5个月的时间,学习人数突破5万,创造行业首个单门课程学习人数奇迹.如此惊人的数据,令笔者不得不对慕课网的课程做了一个小小的深入研究. 视频讲解+在线编程,混合式教学边学边练引领行业平台优势. 在互联网海量的IT学习资源中,慕课网的JAVA课程为何备受学习者青睐,成为5万用户的选择?这和慕课网独家的课程设计有必然关系.视频课程+在线编程实操平台

Docker快速入门以及环境配置详解_docker

前言 数据科学开发环境配置起来让人头疼,会碰到包版本不一致.错误信息不熟悉和编译时间漫长等问题.这很容易让人垂头丧气,也使得迈入数据科学的这第一步十分艰难.而且这也是一个完全不常见的准入门槛. 还好,过去几年中出现了能够通过搭建孤立的环境来解决这个问题的技术.本文中我们就要介绍的这种技术名叫Docker.Docker能让开发者简单.快速地搭建数据科学开发环境,并支持使用例如Jupyter notebooks等工具进行数据探索. 简介 Docker 最初 dotCloud 公司内部的一个业余项目

Android开发入门(一)详解活动 1.2 设置Activity的样式和主题

默认情况下,一个activity占据整个屏幕.然而,可以为一个activity设置一个"对话框"主题,这样, 它就能像一个浮动的对话框显示出来了.举个例子,你可能想要把你的activity定义成一个弹出的"对话框 ",通知用户进行一些操作.在这种情况下,显示一个"对话框"样式的anctivity是一个引起用户注意的好 方法.想要给activity设置主题,很简单,只需在AndroidManifest.xml文件中为<application&

Android开发入门(一)详解活动 1.6 显示复杂对话框

除了"对话框"进度条,也可以创建一个显示"操作进度"的对话框,例如显示正在下载的状态. 1. 创建一个工程:Dialog. 2. main.xml中的代码. <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" andro

Android开发入门(一)详解活动 1.5 显示进度对话框

当要进行耗时的操作的时候,往往会看见"请稍候"字样的对话框.例如,用户正在登入服务器,此时并 不允许用户使用这个软件,或者应用程序把结果返回给用户之前,要进行某些耗时的计算.在这些情况下, 显示一个"进度条"对话框,能友好地让用户等待,同时也能阻止用户进行某些不必要的操作. 1. 创建一个工程:Dialog. 2. main.xml中的代码. <?xml version="1.0" encoding="utf-8"?&g