Asp.net管道模型之(HttpModules 和 HttpHandler)
上一节我们从大概范围介绍了管道模型的整体流程,我们从其中知道管道最重要的两大组件为:HttpModules 跟 HttpHandler。今天我们着重来介绍一下这两大组件
一:asp.net处理管道
从请求进入ASP.NET工作者进程,直至它到达最终的处理程序之前要经过一系列的步骤和过程,这个步骤和过程称为ASP.NET处理管道。
管道模型使用一个HttpContext对象去描述声明request/response信息。这个对象在HttpApplication和handler之间来回传递。HttpContext对象通过属性来描述request和response信息。下图展示了部分HttpContext类常用的属性。
二:Module的详解
1:HttpModule:可以看做是一个拦截器,给我们在特定的事件处理请求的机会。HttpModule有很多应用,例如,我们要在每个请求的页面事件前加载Session数据,那么就用到SessionModule等等;
2:asp.net4.0提供了路由机制也是建立在一个UrlRouteModule上面的,它在请求映射到具体程序前拦截,然后重新映射。MVC又是建立在路由机制的基础上的。
3:怎么查看系统默认自带的Module以及怎么配置自定义的module。
A:打开C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Config中的web.config,然后找到system.web节点下面的HttpModules下Module,一般系统自带的webconfig不要轻易去修改,因为这个是针对于全局的。如果要修改只需要修改自己项目中的webconfig
B:打开项目所在的webconfig,注释见下图:
4:怎么自定义module
右键 -》添加新项 --》选择Asp.net模块即自己定义的一个module已经创建完成。
module的注册一般都是在Init方法里面来完成的,整个过程分为以下几步:
- 当站点第一个资源被访问的时候,Asp.Net会创建HttpApplication类的实例,它代表着站点应用程序,同时会创建所有在Web.Config中注册过的Module实例。
- 在创建Module实例的时候会调用Module的Init()方法。
- 在Init()方法内,对想要作出响应的HttpApplication暴露出的事件进行注册。(仅仅进行方法的简单注册,实际的方法需要另写)。
- HttpApplication在其应用程序周期中触发各类事件。
- 触发事件的时候调用Module在其Init()方法中注册过的方法。
- Dispose():它可以在进行垃圾回收之前进行一些清理工作
下面附上自己新定义的module代码:
1 public class CustomHttpModule : IHttpModule 2 { 3 public void Dispose() 4 { 5 Console.WriteLine(); 6 } 7 8 public event EventHandler CustomHttpModuleHandler; 9 10 /// <summary> 11 /// 注册动作 12 /// </summary> 13 /// <param name="context"></param> 14 public void Init(HttpApplication application) 15 { 16 #region 为每一个事件,都注册了一个动作,向客户端输出信息 17 application.AcquireRequestState += (s, e) => application.Response.Write(string.Format("<h1 style='color:#00f'>来自MyCustomModule 的处理,{0}请求到达 {1}</h1><hr>", DateTime.Now.ToString(), "AcquireRequestState ")); 18 application.AuthenticateRequest += (s, e) => application.Response.Write(string.Format("<h1 style='color:#00f'>来自MyCustomModule 的处理,{0}请求到达 {1}</h1><hr>", DateTime.Now.ToString(), "AuthenticateRequest ")); 19 application.AuthorizeRequest += (s, e) => application.Response.Write(string.Format("<h1 style='color:#00f'>来自MyCustomModule 的处理,{0}请求到达 {1}</h1><hr>", DateTime.Now.ToString(), "AuthorizeRequest ")); 20 application.BeginRequest += (s, e) => application.Response.Write(string.Format("<h1 style='color:#00f'>来自MyCustomModule 的处理,{0}请求到达 {1}</h1><hr>", DateTime.Now.ToString(), "BeginRequest ")); 21 application.Disposed += (s, e) => application.Response.Write(string.Format("<h1 style='color:#00f'>来自MyCustomModule 的处理,{0}请求到达 {1}</h1><hr>", DateTime.Now.ToString(), "Disposed ")); 22 application.EndRequest += (s, e) => application.Response.Write(string.Format("<h1 style='color:#00f'>来自MyCustomModule 的处理,{0}请求到达 {1}</h1><hr>", DateTime.Now.ToString(), "EndRequest ")); 23 application.Error += (s, e) => application.Response.Write(string.Format("<h1 style='color:#00f'>来自MyCustomModule 的处理,{0}请求到达 {1}</h1><hr>", DateTime.Now.ToString(), "Error ")); 24 application.LogRequest += (s, e) => application.Response.Write(string.Format("<h1 style='color:#00f'>来自MyCustomModule 的处理,{0}请求到达 {1}</h1><hr>", DateTime.Now.ToString(), "LogRequest ")); 25 application.MapRequestHandler += (s, e) => application.Response.Write(string.Format("<h1 style='color:#00f'>来自MyCustomModule 的处理,{0}请求到达 {1}</h1><hr>", DateTime.Now.ToString(), "MapRequestHandler ")); 26 application.PostAcquireRequestState += (s, e) => application.Response.Write(string.Format("<h1 style='color:#00f'>来自MyCustomModule 的处理,{0}请求到达 {1}</h1><hr>", DateTime.Now.ToString(), "PostAcquireRequestState ")); 27 application.PostAuthenticateRequest += (s, e) => application.Response.Write(string.Format("<h1 style='color:#00f'>来自MyCustomModule 的处理,{0}请求到达 {1}</h1><hr>", DateTime.Now.ToString(), "PostAuthenticateRequest ")); 28 application.PostAuthorizeRequest += (s, e) => application.Response.Write(string.Format("<h1 style='color:#00f'>来自MyCustomModule 的处理,{0}请求到达 {1}</h1><hr>", DateTime.Now.ToString(), "PostAuthorizeRequest ")); 29 application.PostLogRequest += (s, e) => application.Response.Write(string.Format("<h1 style='color:#00f'>来自MyCustomModule 的处理,{0}请求到达 {1}</h1><hr>", DateTime.Now.ToString(), "PostLogRequest ")); 30 application.PostMapRequestHandler += (s, e) => application.Response.Write(string.Format("<h1 style='color:#00f'>来自MyCustomModule 的处理,{0}请求到达 {1}</h1><hr>", DateTime.Now.ToString(), "PostMapRequestHandler ")); 31 application.PostReleaseRequestState += (s, e) => application.Response.Write(string.Format("<h1 style='color:#00f'>来自MyCustomModule 的处理,{0}请求到达 {1}</h1><hr>", DateTime.Now.ToString(), "PostReleaseRequestState ")); 32 application.PostRequestHandlerExecute += (s, e) => application.Response.Write(string.Format("<h1 style='color:#00f'>来自MyCustomModule 的处理,{0}请求到达 {1}</h1><hr>", DateTime.Now.ToString(), "PostRequestHandlerExecute ")); 33 application.PostResolveRequestCache += (s, e) => application.Response.Write(string.Format("<h1 style='color:#00f'>来自MyCustomModule 的处理,{0}请求到达 {1}</h1><hr>", DateTime.Now.ToString(), "PostResolveRequestCache ")); 34 application.PostUpdateRequestCache += (s, e) => application.Response.Write(string.Format("<h1 style='color:#00f'>来自MyCustomModule 的处理,{0}请求到达 {1}</h1><hr>", DateTime.Now.ToString(), "PostUpdateRequestCache ")); 35 application.PreRequestHandlerExecute += (s, e) => application.Response.Write(string.Format("<h1 style='color:#00f'>来自MyCustomModule 的处理,{0}请求到达 {1}</h1><hr>", DateTime.Now.ToString(), "PreRequestHandlerExecute ")); 36 application.PreSendRequestContent += (s, e) => application.Response.Write(string.Format("<h1 style='color:#00f'>来自MyCustomModule 的处理,{0}请求到达 {1}</h1><hr>", DateTime.Now.ToString(), "PreSendRequestContent ")); 37 application.PreSendRequestHeaders += (s, e) => application.Response.Write(string.Format("<h1 style='color:#00f'>来自MyCustomModule 的处理,{0}请求到达 {1}</h1><hr>", DateTime.Now.ToString(), "PreSendRequestHeaders ")); 38 application.ReleaseRequestState += (s, e) => application.Response.Write(string.Format("<h1 style='color:#00f'>来自MyCustomModule 的处理,{0}请求到达 {1}</h1><hr>", DateTime.Now.ToString(), "ReleaseRequestState ")); 39 application.RequestCompleted += (s, e) => application.Response.Write(string.Format("<h1 style='color:#00f'>来自MyCustomModule 的处理,{0}请求到达 {1}</h1><hr>", DateTime.Now.ToString(), "RequestCompleted ")); 40 application.ResolveRequestCache += (s, e) => application.Response.Write(string.Format("<h1 style='color:#00f'>来自MyCustomModule 的处理,{0}请求到达 {1}</h1><hr>", DateTime.Now.ToString(), "ResolveRequestCache ")); 41 application.UpdateRequestCache += (s, e) => application.Response.Write(string.Format("<h1 style='color:#00f'>来自MyCustomModule 的处理,{0}请求到达 {1}</h1><hr>", DateTime.Now.ToString(), "UpdateRequestCache ")); 42 #endregion 43 } 44 }
自己定义的module已经完成,这样创建完成后,module还不会生效,如果想要其生效,需要在webconfig中的<system.webServer>(因为是该节点是针对于IIS中的集成模式)节点中配置:
<add name="CustomHttpModule" type="Ruanmou.MVC5.Utility.Pipeline.CustomHttpModule,Ruanmou.MVC5"/>
这样既可生效。具体详细配置见上面的第3点
另外还可以这样去新建module,然后注册方法如上配置webconfig,同样浏览的时候页面上面会看到module输出的信息:
1 public class ModuleDemo:IHttpModule 2 { 3 // Init方法仅用于给期望的事件注册方法 4 public void Init(HttpApplication context) { 5 context.BeginRequest += new EventHandler(context_BeginRequest); 6 context.EndRequest += new EventHandler(context_EndRequest); 7 } 8 9 // 处理BeginRequest 事件的实际代码 10 void context_BeginRequest(object sender, EventArgs e) { 11 HttpApplication application = (HttpApplication)sender; 12 HttpContext context = application.Context; 13 context.Response.Write("<h1 style='color:#00f'>来自HttpModule 的处理,请求到达</h1><hr>"); 14 } 15 16 // 处理EndRequest 事件的实际代码 17 void context_EndRequest(object sender, EventArgs e) { 18 HttpApplication application = (HttpApplication)sender; 19 HttpContext context = application.Context; 20 context.Response.Write("<hr><h1 style='color:#f00'>来自HttpModule的处理,请求结束</h1>"); 21 } 22 23 public void Dispose() { 24 } 25 }
注意:module是针对于应用级,而不是页面级别的,即是只要注册module,则程序中所有的页面都会生效的!
5:怎么看当前请求中运用到了哪些module以及事件呢,可以通过下面代码来查看:
1 public ActionResult Module() 2 { 3 HttpApplication app = base.HttpContext.ApplicationInstance; 4 5 #region 得到当前请求中所有的Event事件 6 List<SysEvent> sysEventsList = new List<SysEvent>(); 7 int i = 1; 8 foreach (EventInfo e in app.GetType().GetEvents()) 9 { 10 sysEventsList.Add(new SysEvent() 11 { 12 Id = i++, 13 Name = e.Name, 14 TypeName = e.GetType().Name 15 }); 16 } 17 #endregion 18 19 #region 得到当前HttpApplication中所有的module,包括系统自带跟自己自定义的 20 List<string> list = new List<string>(); 21 foreach (string item in app.Modules.Keys) 22 { 23 list.Add($"{item}: {app.Modules.Get(item)}"); 24 } 25 #endregion 26 27 ViewBag.Modules = string.Join(",", list); 28 return View(sysEventsList); 29 }
6:每个module的功能用途
名称 |
类型 |
功能 |
OutputCache |
System.Web.Caching.OutputCacheModule |
页面级输出缓存 |
Session |
System.Web.SessionState.SessionStateModule |
Session状态管理 |
WindowsAuthentication |
System.Web.Security.WindowsAuthenticationModule |
用集成Windows身份验证进行客户端验证 |
FormsAuthentication |
System.Web.Security.FormsAuthenticationModule |
用基于Cookie的窗体身份验证进行客户端身份验证 |
PassportAuthentication |
System.Web.Security.PassportAuthenticationModule |
用MS护照进行客户身份验证 |
RoleManager |
System.Web.Security.RoleManagerModule |
管理当前用户角色 |
UrlAuthorization |
System.Web.Security.UrlAuthorizationModule |
判断用户是否被授权访问某一URL |
FileAuthorization |
System.Web.Security.FileAuthorizationModule |
判断用户是否被授权访问某一资源 |
AnonymousIdentification |
System.Web.Security.AnonymousIdentificationModule |
管理Asp.Net应用程序中的匿名访问 |
Profile |
System.Web.Profile.ProfileModule |
管理用户档案文件的创立 及相关事件 |
ErrorHandlerModule |
System.Web.Mobile.ErrorHandlerModule |
捕捉异常,格式化错误提示字符,传递给客户端程序 |
三:管道中19个标准事件(每发一次都基本上会走下面的流程)
1:HttpHandler:可以看做一个处理器,它负责处理请求,输出数据。aspx,ashx或者说实现了IHttpHandler的都是HttpHandler。
2:系统的web.config配置的默认的对应的处理器。是根据后缀名然后映射到不同的处理器上面
随便找一个.aspx对应的handler,通过反编译会得到证实
3:自定义Handler处理器
3.1:右键新建项然后选择下图
3.2:然后具体代码如下:
1 public class CustomHandler : IHttpHandler 2 { 3 //MSDN上是这样解释的:获取一个值,该值指示其他请求是否可以使用 IHttpHandler 实例。也就是说后继的Http请求是不是可以继续使用实现了该接口的类的实例,一般来说,我把它设置成true。 4 public bool IsReusable => true; 5 6 public void ProcessRequest(HttpContext context) 7 { 8 //Console.WriteLine("This is AAAA"); 9 context.Response.Write("This is AAAA"); 10 context.Response.ContentType = "text/html"; 11 } 12 }
3.3:然后在 <system.webServer>节点下的 <handlers>增加如下:
1 <handlers> 2 <add name="rtmp" verb="*" path="*.rtmp" type="Ruanmou.MVC5.Utility.Pipeline.CustomHandler,Ruanmou.MVC5"/> 3 </handlers>
如果想要映射多个后缀可以:
<httpHandlers> <add path="*.wss,*.rss" type=" 命名空间.类名,命名空间" verb="GET,POST," /> </httpHandlers>
注意:这里,path指的是请求的文件名称,可以使用通配符扩大范围,也可以明确指定这个handler仅用于处理某个特定的文件(比如说:filename.aspx)的请求。verb指的是请求此文件的方式,可以是post或get,用*代表所有访问方式。type属性由“,”分隔成两部分,第一部分是实现了接口的命名空间.类名,第二部分是位于Bin目录下的编译过的程序集名称。
3.4:最后在App_Start下面的RouteConfig里面配置一下路由过滤器,不然会一直走路由匹配:
routes.IgnoreRoute("CustomService/{*pathInfo}");//以CustomService开头,都不走路由
3.5:这样即可,可以在浏览器输入:
我们可以使用介个方式来做一个图片防盗链的功能。
4:系统自带默认的事件
注意:我们每次请求都会走下面的事件,然后会根据我们请求的后缀名然后去找对应的处理Handler,然后进行处理请求响应。
四:总结
MVC URLRouting Module对进入server的request进行了拦截,然后对request的handlerjinxingltes的处理。asp.net WebForm和asp.net MVC两者的不同,是在于最终使用的IHttpHandle的不同。WebForm中使用的是Page这个Handler,MVC中使用的是MVCHander。