ASP.NET HTTP运行时组成详解(下)
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ASP.NET HTTP运行时组成详解(下)相关的知识,希望对你有一定的参考价值。
参考技术A三 HTTP 管道
ASP NET ISAPI 扩展启动辅助进程后 它将传递部分命令行参数 辅助进程使用这些参数来执行加载 CLR 前需要执行的任务 传递的值包括 和 D 安全性所要求的身份验证等级 可以使用的命名管道的数量和 IIS 进程标识 命名管道的名称是使用 IIS 进程标识和允许的管道数随机生成的 辅助进程不接收可用管道的名称 但可以接收识别管道名称所需的信息
和 D 安全性与 Microsoft? NET Framework 有何关系?实际上 CLR 是作为 对象提供的 更准确地说 CLR 本身不是由 代码构成的 但是指向 CLR 的接口却是一个 对象 因此 辅助进程加载 CLR 的方式与加载 对象的方式相同
当 ASPX 请求遇到 IIS 时 Web 服务器将根据选择的身份验证模型(匿名 Windows Basic 或 Digest)来分配一个令牌 当辅助进程收到要处理的请求时 令牌被传递到辅助进程 请求由辅助进程中的线程获取 该线程从最初获取传入请求的 IIS 线程继承身份令牌 在 aspnet_wp exe 中 负责处理请求的实际帐户取决于在特殊的 ASP NET 应用程序中是如何配置模拟的 如果模拟被禁用(默认设置) 则线程将在辅助进程的帐户下运行 默认情况下 该帐户在 ASP NET 进程模型中为 ASPNET 在 IIS 进程模型中为 NEORKSERVICE 这两个帐户都是 弱 帐户 提供的功能比较有限 可以有效抵挡回复性攻击 (Revert to self Attack) (回复性攻击是指将模拟的客户端的安全性令牌回复到父进程令牌 为辅助进程分配弱帐户可以挫败此类攻击 )
高度概括起来 ASP NET 辅助进程完成的一项主要任务就是将请求交给一系列称为的 HTTP 管道的托管对象 要激活 HTTP 管道 可以创建一个 HttpRuntime 类的新实例 然后调用其 ProcessRequest 方法 如前所述 ASP NET 中始终只运行一个辅助进程(除非启用了 Web Garden 模型) 该进程在独立的 AppDomain 中管理所有的 Web 应用程序 每个 AppDomain 都有自己的 HttpRuntime 类实例 即管道中的输入点 HttpRuntime 对象初始化一系列有助于实现请求的内部对象 Helper 对象包括缓存管理器(Cache 对象)和内部文件系统监视器(用于检测构成应用程序的源文件的更改) HttpRuntime 为请求创建上下文 并用与请求相关的 HTTP 信息填充上下文 上下文用 HttpContext 类的实例来表示
另一个在 HTTP 运行时的设置初期创建的 Helper 对象是文本书写器 用于包含浏览器的响应文本 文本书写器是 HttpWriter 类的实例 此对象对页面代码以编程方式发送的文本进行缓存 HTTP 运行时被初始化后 它将查找实现请求的应用程序对象 应用程序对象是 HttpApplication 类的实例 该类就是 global asax 文件背后的类 global asax 在编程时是可选的 但在构建结构时是必需的 因此 如果应用程序中没有构建类 则必须使用默认对象 ASP NET 运行时包括几个中间工厂类 可以用来查找并返回有效的 Handler 对象以处理请求 整个过程中用到的第一个工厂类是 HttpApplicationFactory 它的主要任务是使用 URL 信息来查找 URL 虚拟目录和汇集的 HttpApplication 对象之间的匹配关系
应用程序工厂类的行为可以概括为以下几点
工厂类维护 HttpApplication 对象池 并使用它们来处理应用程序的请求 池的寿命与应用程序的寿命相同
应用程序的第一个请求到达时 工厂类提取有关应用程序类型的信息(global asax 类) 设置用于监视更改的文件 创建应用程序状态并触发 Application_OnStart 事件
工厂类从池中获取一个 HttpApplication 实例 并将要处理的请求放入实例中 如果没有可用的对象 则创建一个新的 HttpApplication 对象 要创建 HttpApplication 对象 需要先完成 global asax 应用程序文件的编译
HttpApplication 开始处理请求 并且只能在完成这个请求后才能处理新的请求 如果收到来自同一资源的新请求 则由池中的其他对象来处理
应用程序对象允许所有注册的 HTTP 模块对请求进行预处理 并找出最适合处理请求的处理程序类型 这通过查找请求的 URL 的扩展和配置文件中的信息来完成
HTTP 处理程序是一些实现 IHttpHandler 接口的类 NET Framework 为常见的资源类型提供了一些预定义的处理程序 包括 ASPX 页面和 Web 服务 machine config 文件中的 <Handlers> 部分定义了 HttpApplication 对象必须实例化才能处理特定类型资源的请求的类名 如果 Helper 类是一个处理程序工厂 GetHandler 方法将确定要使用的处理程序类型 这时 将从一组类似的对象中获取适当类型的处理程序 并对其进行配置以处理请求
IHttpHandler 接口提供了两个方法 IsReusable 和 ProcessRequest 前者将返回一个布尔值 表示处理程序是否可以被汇集 (大多数预定义的处理程序都是汇集的 但是您可以自行定义每次都需要新实例的处理程序 )ProcessRequest 方法包含处理特定类型资源所需的所有逻辑 例如 ASPX 页面的处理程序基于以下伪代码
private void ProcessRequest() // 确定请求是否是回发 (postback)IsPostBack = DeterminePostBackMode();
// 触发 ASPX 源代码的 Page_Init 事件PageInit();
// 加载 ViewState 处理已发送的值 if (IsPostBack) LoadPageViewState();ProcessPostData();
// 触发 ASPX 源代码的 Page_Load 事件PageLoad();
// ) 再次处理已发送的值(当// 动态创建控件时)// ) 将属性更改的服务器端事件提升为输入驱动的// 控件(即复选框的状态改变)// ) 执行与回发事件相关的所有代码if (IsPostBack) ProcessPostDataSecondTry();RaiseChangedEvents();RaisePostBackEvent();
// 触发 ASPX 源代码的 Page_PreRender 事件PreRender();
// 将控件的当前状态保存到 ViewState 中SavePageViewState();
// 将页面内容呈现给 htmlRenderControl(CreateHtmlTextWriter(Response Output));
无论调用的资源类型如何 基于 HTTP 处理程序的模型是相同的 唯一随资源类型变化而变化的元素是处理程序 HttpApplication 对象负责查找应该使用哪种处理程序来处理请求 HttpApplication 对象还负责检测对动态创建的 表示资源的程序集(如 aspx 页面或 a *** x Web 服务)所进行的更改 如果检测到更改 应用程序对象将确保编译并加载所请求的资源的最新来源
四 临时文件和页面程序集
要全面了解 ASP NET HTTP 运行时 让我们来分析一下当请求 ASP NET 页面时 文件系统层所发生的变化 接下来 您将了解由 HTTP 管道的对象管理和监视的一组动态创建的临时文件
虽然可以将页面的核心代码隔离在代码背后的 C# 或 Microsoft? Visual Basic? NET 类中 但可以将 Web 页面编写和部署为 aspx 文本文件 对于要显示为 URL 的页面来说 aspx 文件在应用程序的 Web 空间中必须始终可用 aspx 文件的实际内容将确定应用程序对象要加载的程序集(或多个程序集)
按照设计 HttpApplication 对象将查找一个根据请求的 ASPX 文件命名的类 如果页面命名为 sample aspx 则要加载的相应的类名为 ASP sample_aspx 应用程序对象在 Web 应用程序的所有程序集文件夹中查找这样的类 这些文件夹包括全局程序集缓存 (GAC) Bin 子文件夹和 Temporary ASP NET Files 文件夹 如果未找到这样的类 HTTP 结构将分析 aspx 文件的源代码 创建一个 C# 或 Visual Basic NET 类(具体创建哪种类 取决于 aspx 页面上设置的语言) 同时对其进行编译 新创建的程序集的名称是随机生成的 位于特定于应用程序的子文件夹中 路径如下所示 C:\\WINDOWS\\Microsoft NET\\Framework\\v \\Temporary ASP NET Files
子文件夹 v 特定于 ASP NET 如果您使用的是 ASP NET 子文件夹的版本号会有所不同 即子文件夹名为 v 再次访问页面时 程序集就已存在 不需要重新创建 但是 HttpApplication 对象是如何确定特定于页面的程序集是否存在呢?它每次都要扫描大量文件夹吗?不 并不是这样
应用程序对象只查看 Temporary ASP NET Files 文件夹中某个特殊文件夹的内容 具体路径(特定于应用程序的路径)由 HttpRuntime CodegenDir 属性返回 如果是第一次访问 aspx 文件(即还未创建页面程序集) 则该文件夹中就不存在以 ASPX 页面名称开头的 XML 文件 例如 具有动态程序集的 sample aspx 页面应有如下的条目
sample aspx XXXXX xml
XXXXX 占位符是一种散列代码 通过读取该 XML 文件的内容 应用程序对象就可以了解要加载的程序集的名称以及要在其中获取的类 以下代码片段是这种 Helper 文件的典型内容 包含 ASP sample_aspx 类的程序集的名称是 mvxvx xr
<preserve assem= mvxvx xr type= ASP sample_aspx ><filedep name= c:\\inetpub\\root\\vdir\\sample aspx /></preserve>
当然 只有在分析 filedep 文件的源代码以生成动态程序集时才创建该文件 对 filedep 文件所做的任何更改都会使程序集无效 在下一次请求时必须重新编译 需要注意的是 在 ASP NET 架构的未来版本中 该实现过程可能会有较大改变 不论什么原因 只要您决定在当前应用程序中使用它 都必须十分小心
由于更新而要为页面创建新的程序集时 ASP NET 将验证是否可以删除旧的程序集 如果旧的程序集只包含修改后的页面的类 ASP NET 将试图删除并替换该程序集 否则将在保留旧程序集的情况下创建一个新程序集
在删除过程中 ASP NET 可能会发现程序集文件已被加载并锁定 这种情况下 可以为旧程序集添加一个 DELETE 扩展名 以将其重新命名 (注意 所有 Windows 文件都可以在使用过程中重新命名 )只要应用程序重新启动(例如 由于对某个应用程序文件如 global asax 和 web config 进行了更改) 这些临时的 DELETE 文件就将被删除 但在处理下一个请求时 ASP NET 运行时不会删除这些文件
请注意 默认情况下 在整个应用程序重新启动之前 每个 ASP NET 应用程序最多可以重新编译 个页面 同时会损失一些会话和应用程序数据 当最近的编译次数超过了 <Runtime> 部分的 numRepilesBeforeAppRestart 属性中设置的阈值时 将卸载 AppDomain 并重新启动应用程序 还要注意 在 NET Framework 中 您无法卸载单个程序集 AppDomain 是可以从 CLR 卸载的最小的代码块
五 小结
ASP NET 应用程序有两大特征 进程模型和页面对象模型 ASP NET 提前使用了 IIS 的一些功能 而 IIS 则是 Windows Server 中提供的全新的 开创性的 Microsoft Web 信息服务 尤其值得一提的是 在独立的辅助进程中运行的 ASP NET 应用程序 其行为与 IIS 中的所有应用程序相同 而且 尽管会出现运行时异常 内存泄露或程序错误 ASP NET 运行时仍能自动回收辅助进程以保证实现卓越的性能 这种功能已成为 IIS 的系统功能
lishixinzhi/Article/program/net/201311/15766
经典ASP.NET MVC3.0入门详解
http://blog.csdn.net/csh624366188/article/details/7064269
:由于本文原在word文档里编写,写本文章时运用了大量截图,直接复制到博客里,没有显示图片,
图片只是一些简单的运行结果截图,不影响大家学习
p.Net MVC已经到第三版了,相信大家也都熟悉了,我也不再重复相关概念性的东西了。但是大家一定要了解,Asp.Net MVC是微软的一 个开源的UI层框架,是AspNet的另外一种开发模式。好废话不多说,那我们开始进入Asp.Net MVC3 的学习中来,工欲善其事,必先利其器! 所以我们必须搭建好自己的开发环境才能为我们下一步的学习和开发提供更好的支持。
那你的机器的必备条件是:
1)VS2010(当然你非要使用VS2008那我也不能说什么了)
2)SqlServer 2000/2005/2008
3)Asp.Net MVC3安装包(应该是需要VS2010SP1)
下面提供一些URL链接方便大家下载学习:
1)Asp.Net MVC3的官网:http://www.asp.net/mvc/mvc3
如下图所示:
这个大家在安装过程中可能时间会非常长(>=20分钟),大家忍耐一下!
2)安装Asp.Net MVC3安装包
http://www.microsoft.com/web/gallery/install.aspx?appid=MVC3
3)Asp.Net MVC3源码开源地址:
http://aspnet.codeplex.com/releases/view/58781
如下图所示:
第二节:第一个Asp.Net MVC3项目
1)创建项目:
2)选择项目的默认视图引擎
我们选择一个Empty模板,然后选择Razor视图引擎(Asp.Net MVC3中提供的新的视图引擎)选择HTML5标记支持打上勾(这块我也不了解,呵呵,希望做过这块研究的高手跟我分享一下!)。
3)创建后的项目:
Asp.Net MVC3貌似跟之前的版本创建的项目模板没什么大的不同,文件夹也基本相似。当然我们看到文件夹内的Jquery的包更新到了1.5.1 。后面的文章中会对每个文件夹都做相关的介绍。在此就不多啰嗦了。
4)添加代码,跑起来我们的第一个Demo
首先:在Controller文件夹上右击,选择添加菜单,然后选择Controller,如下图所示:
然后弹出对话框,将Controller命名为HomeController,注意后面的Controller不要去掉,不然它就不会被识别为Controller了,最后点击添加。如下图所示:
设计器自动为我们生成代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace MvcApplication1.Controllers
{
public class HomeController : Controller
{
//
// GET: /Home/
public ActionResult Index()
{
return View();
}
}
}
下一步我们在Action上添加我们需要的视图,如下所示:
5)在前台页面添加我们自己的Html标签
@{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<h1>Hello! It‘s my first Asp.Net MVC Web!</h1>
注意:红色为我们自己添加的部分
6)最终结果:
在上一篇文章Asp.Net MVC3 简单教程(一)环境准备 中 我简单介绍了Asp.Net MVC3项目的安装和第一个Asp.Net MVC3项目的基本情况。没有详细介绍项目中各个文件夹的作用,以及创建的第一 个页面是怎样运行起来的?还有好多的疑问,那在这篇文章中我们将详细介绍项目中各个文件夹的作用,并真的第一个项目我们简要介绍一下 Asp.Net MVC的URL驱动的是怎么回事。
第一节:Asp.Net MVC3项目介绍
让我们先看一下,一个普通的Asp.Net MVC3项目的样例,如下图所示
跟WebFrom还是有区别的,如果你已经了解Asp.Net MVC2的话,那就感觉异常熟悉了!但还是有些区别的。不管怎样我们都一一介绍一下。
很有意思的事情是即使我们创建一个空的MVC项目,VS也自动帮我们创建以上图所示的目录,这是为何呢?这是由于MVC秉承了“约定大于配置”的思 想,我们在使用Asp.Net MVC3开发项目时也要注意,一定要按照它的约定办事,比如:Controller在返回Action后需要一个View 进行展示(当然是调用了View()方法时),这时候Asp.Net MVC回到Views文件夹下找到Controller名字相同的文件夹下面找到具 体的页面进行渲染,当然如果找不到会去Shared文件夹下去找。看下表所示的就是Asp.Net MVC3中各个文件夹的作用。
文件夹 |
作用 |
/Controllers |
存放控制器类【职责是:处理用户的请求,指挥具体的页面进行渲染交给客户端】 |
/Views |
存放各个控制器对应的视图文件,如果是Razor引擎的话那后缀是cshtml.如果使用的WebFrom的视图引擎的话,那还是Aspx后缀。 |
/Content |
主要存放照片、CSS、Flash等文件 |
/Scripts |
主要存放脚本文件【微软默认给我们提供了JQuery1.5.1的包,看来JQuery已经成为默认的工业标准了!我们没有退路了,呵呵,当然我个人也非常喜欢JQuery】 |
/Models |
主要存放ViewModel类【当然这个不是严格这样要求的,而是推荐你这么做。】 |
其他的几个比较有意思的文件:
一个是Web.Config,另外一个是Global.asax虽然我们大家都非常熟悉,但是跟之前我们WebFrom还是有很多的区别的。 WebConfig文件中,配置了启用客户端脚本验证、配置了System.Web.Routing、System.Web.Mvc等组件。而 Global.asax则在应用启动的时候注册了全局的Area【区域,后面会相信讲解】、全局Filter、路由等。
第二节:Asp.Net MVC的请求处理模型
在上一篇中我们也简单做了个小例子,直接添加一个Controller,然后在Action上添加一个View,直接运行,然后就在我们面前呈现了一个普通的Html页面。那我们详细解释一下这种开发方式或者说开发模型。在讲解之前我们先认识几个概念:
Controller:控制器。在Contrller文件夹添加的以Controller结尾的类就是控制器,它的每个方法就是一个Action。 它的职责是从Model中获取数据,并将数据交给View,它是个指挥家的角色,它并不控制View的显示逻辑,只是将Model的数据交给View,而 具体的怎样展示数据那是View的职责,所以Controller跟View是一个弱耦合的状态,而且Controller可以任意指定具体的View进 行渲染。所以达到了UI层的代码和实体良好的分离。
View:视图.负责数据的展示,当然这个视图代码的编写应该是更接近纯净的Html的,而View层代码的书写又直接跟视图引擎解析的规则有关,所以Razor的语法跟webFrom视图引擎的语法截然不同。而笔者更倾向更喜欢Razor语法的简洁、方便。
Model:很多人把Model理解成领域模型,而MVC本身是一个表现模式,它是更倾向于UI层的一个框架,所以一般我们指定的Model呢在使用时一般作为ViewModel来用,但是总的MVC的思想呢,Model还是领域相关的东西吧。
经过MVC3个模块的了解分析,我们大体也知道了Asp.Net MVC的一些基本的概念。接下来我们分析一个完整的Http的处理过程。看下面一个图:
客户端发送一个Http请求,首先被我们的IIS捕获到,然后根据Url请求的格式,最终交给我们的Route组件,然后它负责解析出我们的Url 具体请求的是哪个Controller下的哪个Action。然后MVC经过处理调用我们的Action执行。在Action中我们一般会从业务的 Façade层取出数据,然后将传输层的数据转换成ViewModel再交给View的视图引擎渲染,最终生成Html的字节流写回客户端。
回到我们第一个项目中的情况是,请求:Http://localhost/Home/Index请 求过来,由Route组件解析出Controller是Home,Action是Index,则通过工厂创建一个Controller的实例,然后调用 InvokeAction方法,执行Index的方法,最终执行View()方法返回一个ViewResult实例,再调用自己的 ExcuteResult方法,将数据上下文和输出流交给视图引擎,然后最终渲染成Html页面交给客户端,最终就看到了我们的第一个页面。
总结一下:
Asp.Net MVC所有的请求都归结到Action上,而且Asp.Net MVC请求--处理--响应的模型非常清晰,而且没有WebFrom那种复杂的生命周期,整个请求处理非常明晰简单,又回归到了最原始的Web开发方式,就是简单的请求处理响应!
前言
前面两篇写的比较简单,刚开始写这个系列的时候我面向的对象是刚开始接触Asp.Net MVC的朋友,所以写的尽量简单。所以写的没多少技术含 量。把这些技术总结出来,然后一简单的方式让更多的人很好的接受这是我一直努力的方向。后面会有稍微复杂点的项目!让我们一起期待吧!
此文我将跟大家介绍一下Asp.Net MVC3 Filter的一些用法。你会了解和学习到全局Fileter,Action Filter等常用用法。
第一节:Filter知识储备
项目大一点总会有相关的AOP面向切面的组件,而MVC(特指:Asp.Net MVC,以下皆同)项目中呢Action在执行前或者执行后我们想 做一些特殊的操作(比如身份验证,日志,异常,行为截取等),而不想让MVC开发人员去关心和写这部分重复的代码,那我们可以通过AOP截取实现,而在 MVC项目中我们就可以直接使用它提供的Filter的特性帮我们解决,不用自己实现复杂的AOP了。
Asp.Net MVC提供了以下几种默认的Filter:
Filter Type |
实现接口 |
执行时间 |
Default Implementation |
Authorization filter |
IAuthorizationFilter |
在所有Filter和Action执行之前执行 |
AuthorizeAttribute |
Action filter |
IActionFilter |
分别在Action执行之前和之后执行。 |
ActionFilterAttribute |
Result filter |
IResultFilter |
分别在Action Result执行之后和之前 |
ResultFilterAttribute |
Exception filter |
IExceptionFilter |
只有在filter, 或者 action method, 或者 action result 抛出一个异常时候执行
|
HandleErrorAttribute |
大家注意一点,Asp.Net MVC提供的ActionFilterAttribute默认实现了IActionFilter和 IResultFilter。而ActionFilterAttribute是一个Abstract的类型,所以不能直接使用,因为它不能实例化,所以我 们想使用它必须继承一下它然后才能使用,下图所示的是ActionFilterAttribute的实现:
所以我们在实现了ActionFilterAttribute,然后就可以直接重写一下父类的方法如下:
public virtual void OnActionExecuted(ActionExecutedContext filterContext);//在Action执行之后执行
public virtual void OnActionExecuting(ActionExecutingContext filterContext); //在Action执行前执行
public virtual void OnResultExecuted(ResultExecutedContext filterContext);//在Result执行之后
public virtual void OnResultExecuting(ResultExecutingContext filterContext); //在Result执行之前
然后我们就可以直接在Action、Result执行之前之后分别做一些操作。
第二节:Action Filter实战
光说不练假把式,那现在我们就直接做一个例子来实际演示一下。
首先我们添加一个普通的类,直接上代码吧:
public class DemoActionAttributeFilter : ActionFilterAttribute
{
public string Message { get; set; }
public override void OnActionExecuted(ActionExecutedContext filterContext)
{ //在Action执行之后执行 输出到输出流中文字:After Action Excute xxx
filterContext.HttpContext.Response.Write(@"<br />After Action Excute" + "\t " + Message);
base.OnActionExecuted(filterContext);
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{ //在Action执行前执行
filterContext.HttpContext.Response.Write(@"<br />Before Action Excute" + "\t " + Message);
base.OnActionExecuting(filterContext);
}
public override void OnResultExecuted(ResultExecutedContext filterContext)
{ //在Result执行之后
filterContext.HttpContext.Response.Write(@"<br />After ViewResult Excute" + "\t " + Message);
base.OnResultExecuted(filterContext);
}
public override void OnResultExecuting(ResultExecutingContext filterContext)
{ //在Result执行之前
filterContext.HttpContext.Response.Write(@"<br />Before ViewResult Excute" + "\t " + Message);
base.OnResultExecuting(filterContext);
}
}
写完这个代码后,我们回到Action上,打上上面的标记如下所示:
[DemoActionAttributeFilter(Message = "action")]
public ActionResult Index()
{ //Action 执行时往输出流写点代码
this.ControllerContext.HttpContext.Response.Write(@"<br />Action Excute");
return Content("Result Excut! ");
}
然后执行F5,页面上则会显示为:
最终我们看到了在Action执行之前和之后都执行了我们的重写的DemoActionAttributeFilter方法,Result执行前后也执行了我们的Filter的方法。
总的执行顺序是:
Action执行前:OnActionExecuting方法先执行→Action执行→OnActionExecuted方法执行 →OnResultExecuting方法执行→返回的ActionRsult中的ExcuteResult方法执行→OnResultExecuted 执行。最终显示的效果就是如上图所示。
感觉很爽吧!呵呵!
如果我们将此标签打到Controller上的话,DemoActionAttributeFilter将作用到Controller下的所有的Action。例如如下代码所示:
[DemoActionAttributeFilter(Message = "controller")]
public class HomeController : Controller
{
[DemoActionAttributeFilter(Message = "action")]
public ActionResult Index()
{
this.ControllerContext.HttpContext.Response.Write(@"<br />Action Excute");
return Content("<br/>Result Excut! ");
}
}
那就有个问题了我们再执行显示的页面会有什么情况呢?Controller上的Filter会执行吗?那标签的作用会执行两次吗?下面是最后的执行结果如下图所示:
结果说明:默认情况下Action上打了DemoActionAttributeFilter 标签后,虽然在Controller上也打上了此标 签,但它只有Action上的标签起作用了。Index 执行时,Filter的方法只执行了一次,而某些情况下我们也想让Controller上的 FilterAttribute也执行一次DemoActionAttributeFilter
那我们怎么才能让Controller上的[DemoActionAttributeFilter(Message = "controller")]也起作用呢?
答案是:我们只需在DemoActionAttributeFilter类的定义上打上标记 [AttributeUsage(AttributeTargets.All, AllowMultiple = true)]即可【下面类的最上面红色 字体部分】,也就是让其成为可以多次执行的Action。代码如下:
[AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
public class DemoActionAttributeFilter : ActionFilterAttribute
{
public string Message { get; set; }
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
filterContext.HttpContext.Response.Write(@"<br />After Action Excute" + "\t " + Message);
base.OnActionExecuted(filterContext);
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
filterContext.HttpContext.Response.Write(@"<br />Before Action Excute" + "\t " + Message);
base.OnActionExecuting(filterContext);
}
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
filterContext.HttpContext.Response.Write(@"<br />After ViewResult Excute" + "\t " + Message);
base.OnResultExecuted(filterContext);
}
public override void OnResultExecuting(ResultExecutingContext filterContext)
{
filterContext.HttpContext.Response.Write(@"<br />Before ViewResult Excute" + "\t " + Message);
base.OnResultExecuting(filterContext);
}
}
然后我们执行的效果如图所示:
我们看到的结果是Controller上的ActionFilter先于Action上打的标记执行。同样Result执行ExcuteResult方法之前也是先执行Controller上的Filter标记中的OnResultExcuteing方法。
最后的执行顺序是:Controller上的OnActionExecuting→Action上的OnActionExecuting→Action执行→Action上的OnActionExecuted→Controller上的OnActionExecuted
到此Action就执行完毕了,我们看到是一个入栈出栈的顺序。后面是Action返回ActionResult后执行了ExecuteResult方法,但在执行之前要执行Filter。具体顺序为:
接上面→Controller的OnResultExecuting方法→Action上的OnResultExecuting→Action返回 ActionResult后执行了ExecuteResult方法→Action上的OnResultExecuted执行→Controller上的 OnResultExecuted执行→结束
第三节:Gloable Filter实战
又接着一个问题也来了,我们想有些公共的方法需要每个Action都执行以下,而在所有的Controller打标记是很痛苦的。幸好Asp。 Net MVC3带来了一个美好的东西,全局Filter。而怎么注册全局Filter呢?答案就在Global.asax中。让我们看以下代码,我是如 何将上面我们定义的DemoActionAttributeFilter 注册到全局Filter中。上代码:
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", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
}
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalFilters.Filters.Add(new DemoActionAttributeFilter() { Message = "Gloable" });
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
}
跟普通的MVC2.0中的Global.asax的区别就是红色部分的代码,我们看到代码中我将自己定义的 DemoActionAttributeFilter的实例加入到GlobalFilters.Filters集合中,然后下面一句就是注册全局 Filter:RegisterGlobalFilters(GlobalFilters.Filters);
这样我们所有的Action和Result执行前后都会调用我们的DemoActionAttributeFilter的重写的方法。
再次运行我们的demo看到的结果是:
我们看到的结果是全局的Action首先执行,然后才是Controller下的Filter执行,最后才是Action上的标签执行。当然这是在 DemoActionAttributeFilter类的定义上打上标记 [AttributeUsage(AttributeTargets.All, AllowMultiple = true)]的前提下。不然 如果 Action打上了标签跟Controller的相同则它只会执行Action上的Filter。
总结
经过这一篇文章的介绍我们大体了解了Filter的使用方法,还了解到全局Filter的用法,尤其是当相同的Filter重复作用到同一个 Action上时,如果没有设置可多次执行的标签那只有Action上的Filter执行,而Controller和全局Filter都被屏蔽掉,但是设 置可多次执行,那首先执行全局Filter其次是Controller再次之就是Action上的Filter了。
今天写点关于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。
第二阶段: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)
{
requestContext.HttpContext.SetSessionStateBehavior(GetSessionStateBehavior(requestContext));
return new MvcHandler(requestContext);
}
.....
}
MvcRouteHandler的GetHttpHandler方法被URLRouteModule调用,而看上面的红色源码部分我们看到,它将请求上下文交给了MVCHandler,并返回了MVCHandler。
而我查看源码得知:MVCHandler实现了IHttpHandler,此时它的ProcessRequest方法被调用。且看MVCHandler的部分源代码:
1 public class MvcHandler : IHttpAsyncHandler, IHttpHandler, IRequiresSessionState
2 {
3 protected internal virtual void ProcessRequest(HttpContextBase httpContext)
4 {
5 SecurityUtil.ProcessInApplicationTrust(() =>
6 {
7 IController controller;
8 IControllerFactory factory;
9 ProcessRequestInit(httpContext, out controller, out factory);//初始化了ControllerFactory
10 try
11 {
12 controller.Execute(RequestContext);
13 }
14 finally
15 {
16 factory.ReleaseController(controller);
17 }
18 });
19 }
20 }
从源码中我们得知:请求交给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
21 public override void ExecuteResult(ControllerContext context)
22 {
23 if (context == null)
24 {
25 throw new ArgumentNullException("context");
26 }
27 if (String.IsNullOrEmpty(ViewName))
28 {
29 ViewName = context.RouteData.GetRequiredString("action");
30 }
31 ViewEngineResult result = null;
32 if (View == null)
33 {
34 result = FindView(context);
35 View = result.View;
36 }
37 TextWriter writer = context.HttpContext.Response.Output;
38 ViewContext viewContext = new ViewContext(context, View, ViewData, TempData, writer);
39 View.Render(viewContext, writer);
40 if (result != null)
41 {
42 result.ViewEngine.ReleaseView(context, View);
43 }
44 }
内部主要是通过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
最后附两张关于此请求管道的两张图,以飨读者。
引子
本文将主要演示怎么将多个Asp.Net MVC项目部署到一个IIS站点中的例子,主要使用的是Asp.Net MVC提供的区域的功能。
Asp.Net MVC提供了区域的功能,可以很方便的为大型的网站划分区域。可以让我们的项目不至于太复杂而导致管理混乱,有了区域后,每个模块 的页面都放入相应的区域内进行管理很方便。而随着项目的复杂,每个开发人员开发的模块呢也可能是一个完整的解决方案,而他要开发的UI项目呢只是主站点项 目的一个区域,而如果把所有的UI项目放到一个UI项目,在团队开发时就不很方便了,而我们想达到的效果是:每个模块都对应一个UI项目【这里指 Asp.Net MVC项目】,最后部署的时候将子项目都配置成区域,而总的项目就是一个站点。
一、项目创建
首先创建一个主Asp.Net MVC项目,然后创建一个子Asp。Net MVC项目。项目的结构如下:
注:
1、AreasDemo【子项目,作为主项目的一个Area】、MvcAppMain【主Web项目】都是普通的Asp.Net MVC3项目
2、MVCControllers是一个类库项目
3、补充:Asp.Net MVC的控制器:Controller是可以放到站点的任何DLL中的,它在搜索控制器时,会搜索站点下的所有DLL, 当类符合条件:不是静态类,类名以Controller结尾,实现了Controller基类【其实最主要是IController接口】的条件时它就会 被识别为控制器。所以我们可以把控制器放到任何的其他项目中,只有将此控制器所在的DLL拷贝到、主站点的Bin目录或者对应的DLL目录就可以了。当然 也可以放在默认的Web项目中的Controller文件夹下。
二、添加测试的Controller和Action
在子区域Web项目AreasDemo项目中添加一个Action,然后添加一个对应的视图
在主Web项目MvcAppMain中添加一个HomeController和相应的Index.cshtml视图文件。
在MVCAppMain项目中添加一个Admin区域,做测试使用。
项目最终截图为:
我们看到,在主站点里添加了一个Admin区域后,默认创建了一个Areas文件夹,而且内部就是存放区域项目的页面。
三、在子项目中添加Areas Registration类
打开AreasDemo项目,添加一个AreasDemoRegistration类文件,其代码如下:
1 public class AreasDemoRegistration : AreaRegistration//在主站点注册区域
2 {
3 public override string AreaName
4 {
5 get { return "AreasDemo"; }
6 }
7 public override void RegisterArea(AreaRegistrationContext context)
8 {
9 context.MapRoute(
10 "AreasDemo_default",//路由名字,必须唯一
11 "AreasDemo/{controller}/{action}/{id}",//路由规则
12 new { action = "Index", id = UrlParameter.Optional }//默认值
13 );
14 }
15 }
其实就是一个普通的类,它实现了AreaRegistration基类。然后我们注册区域路由就会在Global.asax的Application_Start事件方法中去执行注册到主站点的路由表中。具体
可以参考Global.asax中红色代码部分:
16 protected void Application_Start()
17 {
18 AreaRegistration.RegisterAllAreas();//注册所有区域
19 RegisterGlobalFilters(GlobalFilters.Filters);
20 RegisterRoutes(RouteTable.Routes);
21 }
至此我们基本的测试的基础工作都做好了,下面就是到了部署阶段了。
四、部署我们的项目
首先,我们需要将子项目的引用到主项目中。然后我们发布主项目到一个磁盘文件夹。然后,将子项目AreasDemo的Views文件夹拷贝到主项目 发布后的文件夹对应的Areas\AreasDemo文件夹下。其中AreasDemo是areaname,此文件夹需要我们自己手动创建。然后,观察发 布后的bin目录下有没有AreasDemo.dll动态链接库【Web子项目】。
然后,我们将此文件夹发布为IIS里的一个网站。最终演示效果为:
注:这是默认主Web的inde页面
注:这是主站点里添加的Admin区域
注:这是子项目action请求返回的页面
原文出处:点击打开链接
以上是关于ASP.NET HTTP运行时组成详解(下)的主要内容,如果未能解决你的问题,请参考以下文章
HTTP 错误 500.31 - 无法加载 ASP.NET Core 运行时