Asp.net mvc 框架揭秘之Asp.net +Mvc简介

1、什么是MVC?

介绍MVC之前说一下自治视图(Autonomous View):将UI界面的呈现、交互动作的捕捉响应、逻辑处理流程、数据存储等糅合在一起(如WebForm),我们将这种设计模式成为自治视图

自治视图的弊端:

  • 视图和逻辑糅合在一起,不利于逻辑的复用
  • 不利于对UI组件的调试

MVC是基于关注点分离的方针的架构模式,它将一个人机交互用户的功能分为Model、View、Controller三部分,它们各自具有自己的职责:

Model:是对用于状态和业务功能的封装,是同时包含行为和数据的领域模型,Model接收Controller的请求完成相应的业务处理,在状态改变时向View发出通知

View:实现可视化界面的呈现,同时捕获用户的交互操作

Controller:接收View捕获的交互操作,完成相应的UI逻辑,如果需求涉及业务逻辑的需求,则Controller会直接调用Model来处理,在完成UI逻辑处理后,Controller会控制现有View或创建新的View对用户请求予以响应。

Model-View-Controller间的交互关系

其中Controller不单单是介于Model和View之间的中介,Model和View之间亦可以直接交互,当Model中的状态发生改变时,它可以直接通知View,View亦可以主动查询Model中的状态信息。

MVC与"三层结构"的关系?

两者并没有什么可比性,对于多层架构模式来说,MVC通常是被当做UI层的设计模式(Asp.net mvc通常就被认为是UI层的MVC),而Model更多的表现为业务访问层的入口。

2、IIS与asp.net管道

2.1 IIS 5.x与asp.net

IIS 5.x 运行在InetInfo.exe 进程中,该进程中寄宿着一个叫做W3SVC(World Wide Web Publishing Serivice)的windows服务,该服务的功能包括Http请求的监听、工作进程和配置管理(通过元数据(Metadata)获取相关配置信息)

当收到Http请求后,先根据后缀名判断请求的是否是静态资源,如果是则直接以HTTP返回请求的资源,如果是动态资源,则根据扩展名从IIS的脚本映射找到相应的ISAPI动态链接库(DLL)。

ISAPI(Internet Server Application Programing Interface)是一组本地的Win 32 API,是Web应用和IIS之间的纽带,ASP.NET ISAPI 对应的DLL为aspnet_isapi.dll(位于sysdir:\Windows\Microsoft.NET\Framework\versionInfo),ISAPI支持ISAPI (ISAPI Extension)扩展和ISAPI 筛选(ISAPI Filter),前者是真正处理HTTP请求的接口,后者则是在处理请求之前进行验证、转发或拒绝请求等。

如果请求的是基于asp.net类型的资源时,asp.net ISAPI 被加载,aspnet_isapi 会创建asp.net的工作进程(如果该进程尚未启动),对于IIS5.x该进程为asp.net.exe,工作进程初始化的过程中,工作进程的主线程初始化CLR,

对于某个Web请求,CLR创建应用程序域,在应用程序域中http运行时(IsapiRuntime)被加载并创建相应的应用,在IIS5.x中,所有的Web应用都存在于同一工作进程(aspnet_wp.exe)的不同应用程序域中。

2.2 IIS 6.x与Asp.net

IIS 5的在处理http请求上的缺陷:

  1. Asp.net ISAPI 动态链接库被直接加载到InetInfo.exe进程中,它和工作进程之间是基于进程之间的通信,易造成性能上的瓶颈
  2. 所有的Web应用运行在同一工作进程的不同应用程序域中,基于应用程序域的隔离不能从根本上解决不同应用程序间的影响

为解决如上的第一个问题,IIS6.x将aspnet_isapi动态链接库直接加载到工作进程中,针对第二个问题,IIS 6创建应用程序池的机制,我们可以为一个或多个Web应用创建应用程序池,每一个应用程序池对应一个工作进程,为Web应用提供了基于进程的隔离。

注:图中的HTTP.SYS是一个监听HTTP请求的内核驱动,当HTTP.SYS监听到一个HTTP请求后,将其分发给W3SVC, W3SVC解析出请求的URL,并根据Metadata中的URL与Web应用的映射关系定义请求的Web应用,并进一步得到目标应用运行的工作进程,如果尚未创建则为该请求创建工作进程(称之为请求式创建),在工作进程的初始化过程中aspnet_isapi动态链接库被加载。。。。。,之后Asp.net ISAPI加载CLR,创建应用程序域,初始化应用程序。

2.3 IIS 7与 Asp.net

IIS 7 在请求的监听和分发机制上进行了革新性的改进,主要体现在Windows进程激活服务WAS(Windows Process Activation Service)的引进,将IIS6中W3SVC服务承载的部分功能分流给了WAS,W3SVC承载的功能主要有三个部分:

  1. HTTP请求接收:接收HTTP.SYS分发的HTTP请求
  2. 配置管理:从Metadata中加载配置信息配置相关组件
  3. 进程管理:进程的创建、回收、监控进程

IIS 7 将后两个功能分给了WAS,而HTTP请求的接收仍然由W3SVC,WAS的引入为IIS 7提供了对非http协议的支持,WAS通过监听器适配器接口抽象出不同协议监听器(asp.net mvc 4框架解密 p17)

不论是从W3SVC接收到的http请求,还是从WCF监听适配器接收到的请求,最终都会提交到WAS来处理,如果相应的工作进程尚未建立,则创建,否则将请求分发给其它的进程进行后续处理,WAS在请求处理过程中,通过相应的配置管理模块加载配置信息,对相关组件进行配置,与IIS5与IIS 6不同的是,IIS7的配置信息并不保存在元数据中,而是保存在xml格式的配置文件中,基本的配置在applicationHost.config中。

3、ASP.NET 管道

HTTP.SYS 接收到HTTP请求后,如果该请求是对该Web应用的第一次请求时,aspnet_isapi加载CLR,CLR会通过APPDomainFactory为该Web应用创建应用程序域,之后一个叫做IsapiRuntime的运行时被加载,被加载的IsapiHttpRuntime会接收该请求,IsapiHttpRuntime会首先创建一个ISAPIWorkerRequest(HttpWorker类的子类,所有的HTTP请求都必须封装到一个HTTPWorkerRequest对象中才能进行后续处理)的对象,并将该对象传递给Asp.net 的运行时HttpRuntime,此时HTTP请求进入Asp.net 管道,HttpRuntime会根据IsapiWorkerRequest对象创建用于当前请求上下文对象HTTPContext对象

当成功创建HttpContext对象后,HttpRuntime会利用HttpApplicationFactory创建新的或获取一个HttpApplication对象,HttpApplicationFactory内部维护着一个HttpApplication对象池,HttpRuntime从池中选取HttpApplication对象来处理Http请求,处理完成后将对象释放会对象池中(每个HttpApplication只能处理一次请求)

在HttpApplication对象的初始化过程中,会根据配置文件加载并初始化已注册的HttpModule对象,对于HttpApplication来说,在处理Http请求的不同阶段会触发不同的事件,而HttpModule就是将这些事件的操作注入到请求处理的过程中(通过注册相应的HttpApplication事件),HttpApplication具有19个管道事件,分别如下:

Asp.Net 的许多功能如身份验证、缓存、授权都是通过HttpModule实现的

有关HttpModule和HttpApplication事件注册:http://www.cnblogs.com/kissdodog/p/3527922.html

真正的Http请求处理是由HttpHandler。对于不同的资源具有不同的HttpHandl(但都实现了IHttpHandler这个接口,IHttpHandler的ProcessRequest方法请求处理的具体实现),例如Web Form对应的HttpHandler为System.Web.PageUI,asp.net Mvc对应的HttpHandler为mvchandler.

HttpApplication是整个Web应用处理的核心,当收到第一次请求后,Asp.net 会创建多个HttpApplication对象,并将其放入池中,选择一个来处理当前请求,处理完成后不是直接回收,而是释放到HttpApplication对象池中,供后续请求使用,如果池中的所有HttpApplication对象都处于繁忙状态,则创建新的HttpApplication对象。

针对HttpApplication管道事件的HttpHandler注册有两种方式,一是通过Web.config 配置文件实现,二是通过Global.asmx文件来实现

方法一:

  1. 首先定义一个类实现IHttpModule接口,IHttpModule接口的定义如下:

system.web配置元素的子元素httpModules用来配置网站所使用的HttpModule;httpModules的子元素add用来增加一个新的HttpModule;clear将清除前面注册的所有HttpModule。

  add元素有两个必选的属性name和type,简介如下:

  • name表示这个HttpModule在程序中的名字,在网站应用程序中,可以通过这个名字来找到HttpModule对象的引用。HttpApplication的Modules属性表示这个对象所关联的所有HttpModule对象,通过这个name作为索引器,可以找到对应的HttpModule对象。
  • type表示HttpModule对象的类型名,Asp.net网站可以使用这个类型名,通过反射来动态创建HttpModule对象。类型的写法就是反射中要求的类型名称写法,如果这个类定义在网站中,那么,就是一个包含命名空间的类的全名,否则,在全名的后面,使用逗号(,)分隔,还需要跟上类型所在的程序集的名称,这个程序集的名称不需要包含.dll扩展名。

方法二:通过Global.asmx 文件来实现

    在global.asax中,针对HttpApplication的事件处理,可以通过定义特殊命名的方法来实现。首先,这些方法必须符合System.EventHandler,因为所有的HttpApplication管道事件都使用这个委托定义。第二,方法的作用域必须是public。第三,方法的命名格式必须如下:Application_注册的事件名称。按照这种命名方法定义在global.asax中的方法将被自动注册到对应的事件中。

例如,希望在global.asax中注册PostAuthenticateRequest事件处理,那么在global.asax中应该定义一个如下的方法:

4、Asp.Net MVC 整体处理流程

4.1 URL解析

针对Asp.net mvcWeb应用来说,对Http的请求处理是通过Controller相应的Action方法来实现的,因此Asp.net mvc 的首要任务就是从请求URL中解析出相应的Controller和Action,在Asp.net mvc中URL的解析是通过UrlRoutingModule来实现的。UrlRoutingModule实现了IHttpModule接口,在其的Init()方法中,注册HttpApplication管道事件的第7个事件PostResolveRequtestCache

如下为PostResolveRequestCache 的主要处理代码:

在此期间主要做了几件事:

  1. 将封装有当前请求信息HttpContext对象进一步封装为HttpContextWraper(为HttpContextBase的子类)对象,并将其作为参数传入PostResolveRequestCache方法中
  2. 调用RouteCollection(内部具有一个保存有当前已注册的路由信息的Dictionary集合)的GetRouteData(),如果匹配成功,就返回一个封装有匹配到的Route对象的RouteData对象,否则,就返回null,
  3. 将获取到的RouteData和HttpContext进一步封装为RequestContext对象
  4. 将上步封装好的RequestContext对象作为参数调用RouteDatad的GetRouteHandler属性获取一个IRouteHandler类型的对象(MvcRouteHander)
  5. 调用IRouteHandler的GetHttpHander方法,返回一个IHttpHander类型的对象(mvcHandler)

4.2 Controller的激活

UrlRoutingModule通过路由表解析当前的Http请求后得到一个用于封装路由数据的RouteData,之后调用RouteHandler的GetHttpHandler方法得到HttpHandler对象并注册到Http上下文中,由于RouteData的RouteHandler来自Route的RouteHander,默认情况下是一个mvcrouteHander,所以默认情况下用于处理mvc请求的就是这个mvchandler(可以查看RouteCollectionExtension的maproute方法来确认),Controller对象的激活和执行就是在mvcHandler中进行的。

MvcHandler 实现了IHttpHandler接口,在其ProcessRequest方法中实现了Controller的激活和执行

PR方法中的代码如下:

主要功能的实现在封装的内部函数ProcessRequestInit()中,方法如下

其中主要做了如下几件事:

  1. 从参数中获取当前请求的Http上下文(参数中传入的是一个HttpContextWraper)
  2. 从RequestContext(封装的RouteData和HttpContext)中获取Controller名称
  3. 通过ControllerBuilder(用来设置或获取Controller对象工厂)对象中获取创建Controller的工厂对象(默认为DefaultControllerFactory对象)
  4. 调用工厂对象的CreateController方法根据RequestContext和控制器名称创建Controller对象(通过反射)

4.3 Action的执行

Action的执行是在Controller的Excute(RequestContext context)方法中执行的(Controller的Excute()方法是在mvcHandler的ProcessRequest()方法中执行的(见PR方法截图)),其核心在于对应Action方法的执行和作为方法返回的ActionResult的执行

注意:ControllerBase和Controller都实现了IController接口,而Controller又继承自ControllerBase,在ControllerBase中显式实现了接口IController

在IController的Excute方法中调用的同名的实例方法Excute(RequestContext requestContext),

可以看出,实例方法Excute的主要操作有调用了ControllerBase的ExcuteCore()方法,而该方法是一个虚方法,所以任何继承ControllerBase的类都必须重写该方法,在Controller方法中并没有显式实现IController的Excute(可能说的有歧义,这里指在Controller继承了ControllerBase的Excute方法,并没有自己实现),而是重写ExcuteCore方法,重写的Excute方法如下:

首先调用GetActionName方法获取请求的action名称,其后调用ActionIInvoker的InvokeAction方法,这个方法主要做了如下的几件事:

  1. 执行Action过滤器方法
  2. 执行Action方法
  3. 执行作为方法返回的ActionResult

如果这个ActionResult是ViewResult的话,就调用相应的视图引擎渲染视图

以上就是Asp.net mvc 的一次整体请求处理流程。

时间: 2024-10-12 10:26:39

Asp.net mvc 框架揭秘之Asp.net +Mvc简介的相关文章

学习ASP.NET MVC5框架揭秘笔记-ASP.NET MVC是如何运行的(四)

Action的执行 作为Controller的基类ControllerBase,它的Execute方法主要作用在于执行目标Action方法.如果目标Action方法返回一个ActionResult对象,它还需要执行该对象来对当前请求予以响应.在ASP.NET MVC框架中,两者的执行是通过一个叫做ActionInvoker的对象来完成的. 1.ActionInvoker 我们同样为ActionInvoker定义了一个借口IActionInvoker.如下所示.该接口定义了唯一的方法InvokeA

学习ASP.NET MVC5框架揭秘笔记-ASP.NET MVC是如何运行的(一)

ASP.NET MVC是如何运行的 ASP.NET由于采用了管道式设计,所以具有很好的扩展性,整个ASP.NET MVC应用框架就是通过扩展ASP.NET实现的.通过上面对ASP.NET管道设计的介绍我们知道,ASP.NET 的扩展点主要体现在HttpModule和HttpHandler这两个核心组件之上,整个ASP.NET MVC框架就是通过自定义的HttpModule和HttpHandler建立起来的. 接下来我们通过自定义组件来模拟ASP.NET MVC的运行原理. 1.4.1建立在迷你版

学习ASP.NET MVC5框架揭秘笔记-ASP.NET MVC路由(五)

2.AreaRegistration的缓存 Area的注册(主要是基于Area的路由映射注册)通过具体的AreaRegistration来完成.在应用启动的时候,ASP.NET MVC会遍历通过调用BuildManager的静态方法GetReferencedAssemblies得到的程序集列表,并从中找到所有AreaRegistration类型.如果一个应用涉及太多的程序集,则这个过程可能会耗费很多时间.为了提高性能,ASP.NET MVC会对解析出来的所有AreaRegistration类型列

学习ASP.NET MVC5框架揭秘笔记-ASP.NET MVC是如何运行的(三)

Controller的激活 ASP.NET MVC的路由系统通过注册的路由表对当前HTTP请求实施路由解析,从而得到一个用于封装路由数据的RouteData对象,这个过程是通过自定义的UrlRoutingModule对HttpApplication的PostResolveRequestCache事件进行注册实现的.由于得到的RouteData对象中已经包含了目标Controller的名称,我们需要根据该名称激活对应的Controller对象. 1.MvcRouteHandler 通过前面的介绍我

学习ASP.NET MVC5框架揭秘笔记-ASP.NET MVC是如何运行的(五)

完整的流程 对于我们创建的这个迷你版的ASP.NET MVC框架来说,虽然很多细节被直接忽略掉,但是它基本上能够展现整个ASP.NET MVC框架的全貌,支持这个开发框架的核心对象可以说一个不少.接下来我们对通过这个模拟框架展现出来的ASP.NET MVC针对请求的处理流程作一个简单的概括. 由于UrlRoutingModule这个HttpModule被注册到Web应用中,所以对于每个抵达的请求来说,当代表当前应用的HttpApplication对象的PostResolveRequestCach

学习ASP.NET MVC5框架揭秘笔记-ASP.NET MVC是如何运行的(二)

路由 对于一个ASP.NET MVC应用来说,针对HTTP请求的处理实现在目标Controller类型的某个Action,每个HTTP请求不在像ASP.NET Web Forms应用一样是针对一个物理文件,而是针对某个Controller的某个Action方法.目标Controller和Action的名称由HTTP请求的URL来决定,当ASP.NET MVC接收到抵达的请求后,其首要任务就是通过当前HTTP请求解析得到目标Controller和Action的名称,这个过程是通过ASP.NET M

学习ASP.NET MVC5框架揭秘笔记-ASP.NET路由(二)

实例演示:通过路由实现请求地址与.aspx页面的映射 我们创建一个简单的ASP.NET Web Forms应用,并采用一套独立于.aspx文件路径的URL来访问对应的Web页面,两者之间的映射通过路由来实现,我们依然沿用员工管理的场景. 首先我们将员工的所有信息(ID.姓名.性别.出生日期和所在部门)定义在如下所示的Employee类型中,然后定义一个EmployeeRepository类型来维护员工列表的数据.简单起见,员工列表通过静态字段employees表示.EmployeeReposit

学习ASP.NET MVC5框架揭秘笔记-ASP.NET MVC路由(二)

2.2.2 路由注册 ASP.NET MVC通过调用代表全局路由表的RouteCollection对象的扩展方法MapRoute进行路由注册.我们来进行一个简单的实例演示.我们依然沿用之前关于获取天气信息的路由模板,看看通过这种方式注册的Route对象针对匹配的请求将返回怎样一个RouteData对象. 我们创建一个空的ASP.NET Web程序,并手动添加"System.Web.Mvc.dll"和"System.Web.WebPages.Razor.dll"的引用

学习ASP.NET MVC5框架揭秘笔记-ASP.NET MVC路由(三)

2.2.3缺省URL参数 当通过VisualStudio的ASP.NET MVC项目模板创建一个Web应用后,它会为我们注册如下一个模板为"{controller}/{action}/{id}"的默认Route对象.3个路由模板均有相应额默认值.但是变量名为id的默认值为URLParameter.Optional.按照字面的意思,我们将其称为可缺省URL参数.那么将路由变量的默认值进行如此设置与设置一个具体的默认值有什么区别呢? routes.MapRoute( name: "