ASP.NET管道处理模型(一)

  • A+
所属分类:.NET技术
摘要

本章将和大家分享ASP.NET中的管道处理模型。所谓管道处理模型,其实就是后台如何处理一个Http请求,定义多个事件完成处理步骤,每个事件可以扩展动作(IHttpModule), 最后有个IHttpHandler完成请求的处理,这个过程就是管道处理模型。

本章将和大家分享ASP.NET中的管道处理模型。

所谓管道处理模型,其实就是后台如何处理一个Http请求,定义多个事件完成处理步骤,每个事件可以扩展动作(IHttpModule), 最后有个IHttpHandler完成请求的处理,这个过程就是管道处理模型。

还有一个全局的上下文环境HttpContext,无论参数、中间结果、最终结果,都保存在其中。

下面我们将结合部门源码(通过ILSpy反编译得到)进行讲解:

首先我们先来看下 请求到程序响应 的示例图:

ASP.NET管道处理模型(一)

从图中可以看出Http请求需要经过一系列的步骤才会进入到我们的ASP.NET入口System.Web.HttpRuntime.ProcessRequest(HttpWorkerRequest wr)

接下来我们就从请求进入ASP.NET入口开始讲解:

我们通过反编译工具ILSpy找到ASP.NET的入口System.Web.HttpRuntime.ProcessRequest(HttpWorkerRequest wr):

ASP.NET管道处理模型(一)

// System.Web.HttpRuntime [AspNetHostingPermission(SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Medium)] public static void ProcessRequest(HttpWorkerRequest wr) {     if (wr == null)     {         throw new ArgumentNullException("wr");     }     if (HttpRuntime.UseIntegratedPipeline)     {         throw new PlatformNotSupportedException(SR.GetString("Method_Not_Supported_By_Iis_Integrated_Mode", new object[]         {             "HttpRuntime.ProcessRequest"         }));     }     HttpRuntime.ProcessRequestNoDemand(wr); }

接着我们沿 HttpRuntime.ProcessRequestNoDemand(wr)  一直往里找:

会找到System.Web.HttpRuntime.ProcessRequestInternal(HttpWorkerRequest wr)方法,如下所示:

// System.Web.HttpRuntime private void ProcessRequestInternal(HttpWorkerRequest wr) {     Interlocked.Increment(ref this._activeRequestCount);     if (this._disposingHttpRuntime)     {         try         {             wr.SendStatus(503, "Server Too Busy");             wr.SendKnownResponseHeader(12, "text/html; charset=utf-8");             byte[] bytes = Encoding.ASCII.GetBytes("<html><body>Server Too Busy</body></html>");             wr.SendResponseFromMemory(bytes, bytes.Length);             wr.FlushResponse(true);             wr.EndOfRequest();         }         finally         {             Interlocked.Decrement(ref this._activeRequestCount);         }         return;     }     HttpContext httpContext;     try     {         httpContext = new HttpContext(wr, false);     }     catch     {         try         {             wr.SendStatus(400, "Bad Request");             wr.SendKnownResponseHeader(12, "text/html; charset=utf-8");             byte[] bytes2 = Encoding.ASCII.GetBytes("<html><body>Bad Request</body></html>");             wr.SendResponseFromMemory(bytes2, bytes2.Length);             wr.FlushResponse(true);             wr.EndOfRequest();             return;         }         finally         {             Interlocked.Decrement(ref this._activeRequestCount);         }     }     wr.SetEndOfSendNotification(this._asyncEndOfSendCallback, httpContext);     HostingEnvironment.IncrementBusyCount();     try     {         try         {             this.EnsureFirstRequestInit(httpContext);         }         catch         {             if (!httpContext.Request.IsDebuggingRequest)             {                 throw;             }         }         httpContext.Response.InitResponseWriter();         IHttpHandler applicationInstance = HttpApplicationFactory.GetApplicationInstance(httpContext);         if (applicationInstance == null)         {             throw new HttpException(SR.GetString("Unable_create_app_object"));         }         if (EtwTrace.IsTraceEnabled(5, 1))         {             EtwTrace.Trace(EtwTraceType.ETW_TYPE_START_HANDLER, httpContext.WorkerRequest, applicationInstance.GetType().FullName, "Start");         }         if (applicationInstance is IHttpAsyncHandler)         {             IHttpAsyncHandler httpAsyncHandler = (IHttpAsyncHandler)applicationInstance;             httpContext.AsyncAppHandler = httpAsyncHandler;             httpAsyncHandler.BeginProcessRequest(httpContext, this._handlerCompletionCallback, httpContext);         }         else         {             applicationInstance.ProcessRequest(httpContext);             this.FinishRequest(httpContext.WorkerRequest, httpContext, null);         }     }     catch (Exception e)     {         httpContext.Response.InitResponseWriter();         this.FinishRequest(wr, httpContext, e);     } }

从源码可以看出首先它是使用HttpWorkerRequest打包出一个HttpContext,然后再使用HttpContext创建一个IHttpHandler实例,最后用这个IHttpHandler实例来处理请求。

接下来我们沿着  HttpApplicationFactory.GetApplicationInstance(httpContext) 往里找:

// System.Web.HttpApplicationFactory internal static IHttpHandler GetApplicationInstance(HttpContext context) {     if (HttpApplicationFactory._customApplication != null)     {         return HttpApplicationFactory._customApplication;     }     if (context.Request.IsDebuggingRequest)     {         return new HttpDebugHandler();     }     HttpApplicationFactory._theApplicationFactory.EnsureInited();     HttpApplicationFactory._theApplicationFactory.EnsureAppStartCalled(context);     return HttpApplicationFactory._theApplicationFactory.GetNormalApplicationInstance(context); }

其中 HttpApplicationFactory._theApplicationFactory.EnsureAppStartCalled(context)  这句话就是用来启动我们的网站完成项目初始化的,它会去调用我们的Global.asax里面的Application_Start方法。

我们继续往  HttpApplicationFactory._theApplicationFactory.GetNormalApplicationInstance(context) 里面找:

// System.Web.HttpApplicationFactory private HttpApplication GetNormalApplicationInstance(HttpContext context) {     HttpApplication httpApplication = null;     if (!this._freeList.TryTake(out httpApplication))     {         httpApplication = (HttpApplication)HttpRuntime.CreateNonPublicInstance(this._theApplicationType);         using (new ApplicationImpersonationContext())         {             httpApplication.InitInternal(context, this._state, this._eventHandlerMethods);         }     }     if (AppSettings.UseTaskFriendlySynchronizationContext)     {         httpApplication.ApplicationInstanceConsumersCounter = new CountdownTask(1);         Task arg_8A_0 = httpApplication.ApplicationInstanceConsumersCounter.Task;         Action<Task, object> arg_8A_1;         if ((arg_8A_1 = HttpApplicationFactory.<>c.<>9__34_0) == null)         {             arg_8A_1 = (HttpApplicationFactory.<>c.<>9__34_0 = new Action<Task, object>(HttpApplicationFactory.<>c.<>9.<GetNormalApplicationInstance>b__34_0));         }         arg_8A_0.ContinueWith(arg_8A_1, httpApplication, TaskContinuationOptions.ExecuteSynchronously);     }     return httpApplication; }

可以看到该方法就是为了得到一个HttpApplication的实例,但是它并不是简单的创建HttpApplication的实例,HttpApplication有可能是重用的(对象池--Stack--会重用)

我们点击HttpApplication进去看下:

ASP.NET管道处理模型(一)

可以看到它是实现 IHttpHandler和IHttpAsyncHandler 接口的。

到这里我们大概知道,任何一个Http请求一定是有一个IHttpHandler来处理的,任何一个Http请求就是一个HttpApplication对象来处理

我们知道处理请求的过程一般包括固定步骤,例如:权限认证/缓存处理/Session处理/Cookie处理/生成html/输出客户端等,

与此同时,千千万万的开发者,又有各种各样的扩展诉求,任何一个环节都有可能要扩展,该怎么设计?

这里用的是观察者模式,把固定的步骤直接写在Handler里面,在步骤前&后分别放一个事件, 然后开发者可以对事件注册动作,等着请求进来了,然后就可以按顺序执行一下。

HttpApplication里面定义了一系列的事件,最终会按一定的顺序去执行这些事件,我们可以通过反编译工具来看下这些事件的执行顺序。

通过反编译工具找到System.Web.HttpApplication.ProcessEventSubscriptions方法(处理事件订阅的方法):

// System.Web.HttpApplication private void ProcessEventSubscriptions(out RequestNotification requestNotifications, out RequestNotification postRequestNotifications) {     requestNotifications = (RequestNotification)0;     postRequestNotifications = (RequestNotification)0;     if (this.HasEventSubscription(HttpApplication.EventBeginRequest))     {         requestNotifications |= RequestNotification.BeginRequest;     }     if (this.HasEventSubscription(HttpApplication.EventAuthenticateRequest))     {         requestNotifications |= RequestNotification.AuthenticateRequest;     }     if (this.HasEventSubscription(HttpApplication.EventPostAuthenticateRequest))     {         postRequestNotifications |= RequestNotification.AuthenticateRequest;     }     if (this.HasEventSubscription(HttpApplication.EventAuthorizeRequest))     {         requestNotifications |= RequestNotification.AuthorizeRequest;     }     if (this.HasEventSubscription(HttpApplication.EventPostAuthorizeRequest))     {         postRequestNotifications |= RequestNotification.AuthorizeRequest;     }     if (this.HasEventSubscription(HttpApplication.EventResolveRequestCache))     {         requestNotifications |= RequestNotification.ResolveRequestCache;     }     if (this.HasEventSubscription(HttpApplication.EventPostResolveRequestCache))     {         postRequestNotifications |= RequestNotification.ResolveRequestCache;     }     if (this.HasEventSubscription(HttpApplication.EventMapRequestHandler))     {         requestNotifications |= RequestNotification.MapRequestHandler;     }     if (this.HasEventSubscription(HttpApplication.EventPostMapRequestHandler))     {         postRequestNotifications |= RequestNotification.MapRequestHandler;     }     if (this.HasEventSubscription(HttpApplication.EventAcquireRequestState))     {         requestNotifications |= RequestNotification.AcquireRequestState;     }     if (this.HasEventSubscription(HttpApplication.EventPostAcquireRequestState))     {         postRequestNotifications |= RequestNotification.AcquireRequestState;     }     if (this.HasEventSubscription(HttpApplication.EventPreRequestHandlerExecute))     {         requestNotifications |= RequestNotification.PreExecuteRequestHandler;     }     if (this.HasEventSubscription(HttpApplication.EventPostRequestHandlerExecute))     {         postRequestNotifications |= RequestNotification.ExecuteRequestHandler;     }     if (this.HasEventSubscription(HttpApplication.EventReleaseRequestState))     {         requestNotifications |= RequestNotification.ReleaseRequestState;     }     if (this.HasEventSubscription(HttpApplication.EventPostReleaseRequestState))     {         postRequestNotifications |= RequestNotification.ReleaseRequestState;     }     if (this.HasEventSubscription(HttpApplication.EventUpdateRequestCache))     {         requestNotifications |= RequestNotification.UpdateRequestCache;     }     if (this.HasEventSubscription(HttpApplication.EventPostUpdateRequestCache))     {         postRequestNotifications |= RequestNotification.UpdateRequestCache;     }     if (this.HasEventSubscription(HttpApplication.EventLogRequest))     {         requestNotifications |= RequestNotification.LogRequest;     }     if (this.HasEventSubscription(HttpApplication.EventPostLogRequest))     {         postRequestNotifications |= RequestNotification.LogRequest;     }     if (this.HasEventSubscription(HttpApplication.EventEndRequest))     {         requestNotifications |= RequestNotification.EndRequest;     }     if (this.HasEventSubscription(HttpApplication.EventPreSendRequestHeaders))     {         requestNotifications |= RequestNotification.SendResponse;     }     if (this.HasEventSubscription(HttpApplication.EventPreSendRequestContent))     {         requestNotifications |= RequestNotification.SendResponse;     } }

通过上面的源码,我们就能很清楚的看出各个事件的执行顺序了。

下面我们可以通过一张图来更直观的了解这些事件的执行顺序,如下所示:

ASP.NET管道处理模型(一) 

而对HttpApplication里面的事件进行动作注册的,就叫IHttpModule,下面我们就来看下如何实现一个自定义HttpModule

首先我们先来看下Demo的目录结构:

ASP.NET管道处理模型(一)

本Demo的Web项目为ASP.NET Web 应用程序(目标框架为.NET Framework 4.5) MVC项目。 

其中Home控制器:

using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc;  namespace AspNetPipeline.Controllers {     /// <summary>     /// 1 Http请求处理流程     /// 2 HttpApplication的事件     /// 3 HttpModule     /// 4 Global事件     ///      /// Runtime--运行时     /// Context--上下文     /// 任何一个Http请求一定是有一个IHttpHandler来处理的 ashx aspx.cs  MvcHttpHandler      /// 任何一个Http请求就是一个HttpApplication对象来处理     /// 然后处理过程固定包含:权限认证/缓存处理/Session处理/Cookie处理/生成html/输出客户端     /// 与此同时,千千万万的开发者,又有各种各样的扩展诉求,任何一个环节都有可能要扩展,该怎么设计?     /// 这里用的是观察者模式,把固定的步骤直接写在Handler里面,在步骤前&后分别放一个事件,     /// 然后开发者可以对事件注册动作,等着请求进来了,然后就可以按顺序执行一下     ///       /// 对HttpApplication里面的事件进行动作注册的,就叫IHttpModule     /// 自定义一个HttpModule--配置文件注册--然后任何一个请求都会执行Init里面注册给Application事件的动作     /// 学习完HttpModule,我们可以做点什么有用的扩展?     ///  1 日志-性能监控-后台统计数据     ///  2 权限     ///  3 缓存     ///  4 页面加点东西     ///  5 请求过滤--黑名单     ///  6 MVC--就是一个Module扩展     ///       /// 不适合的(不是全部请求的,就不太适合用module,因为有性能损耗)     ///  1 跳转到不同界面--也不适合     ///  2 防盗链--针对一类的后缀来处理,而不是全部请求--判断--再防盗链     ///       /// HttpModule里面发布一个事件CustomHttpModuleHandler,在Global.asax增加一个动作,     /// MyCustomHttpModule_CustomHttpModuleHandler(配置文件module名称_module里面事件名称),请求响应时,该事件会执行     ///       /// HttpModule是对HttpApplication的事件注册动作,而Global则是对HttpModule里面的事件注册动作     ///      ///      /// C:WindowsMicrosoft.NETFramework64v4.0.30319Configweb.config     /// .NetFramework安装路径,是一个全局的配置,是当前电脑上任何一个网站的默认配置,不要去修改它     ///      ///      /// 1 HttpHandler及扩展,自定义后缀,图片防盗链等     /// 2 RoutingModule,IRouteHandler、IHttpHandler     /// 3 MVC扩展Route,扩展HttpHandle     ///      /// 配置文件指定映射关系:后缀名与处理程序的关系(IHttpHandler---IHttpHandlerFactory)     /// Http任何一个请求一定是由某一个具体的Handler来处理的,不管是成功还是失败     /// 以前写aspx,感觉请求访问的是物理地址,其实不然,请求的处理是框架设置的     ///      /// 所谓管道处理模型,其实就是后台如何处理一个Http请求,定义多个事件完成处理步骤,每个事件可以扩展动作(HttpModule),     /// 最后有个HttpHandler完成请求的处理,这个过程就是管道处理模型。     /// 还有一个全局的上下文环境HttpContext,无论参数、中间结果、最终结果,都保存在其中。     ///      /// 自定义Handler处理,就是可以处理各种后缀请求,可以加入自己的逻辑     /// 如果没有--请求都到某个页面--传参数---返回图片     /// 防盗链---加水印---伪静态---RSS--robot--trace.axd     ///      /// MVC里面不是controller action?其实是由 MvcHandler来处理请求,期间完成对action调用的     /// 网站启动时---对RouteCollection进行配置     /// 把正则规则和RouteHandler(提供HttpHandler)绑定,放入RouteCollection,     /// 请求来临时---用RouteCollection进行匹配     /// 所谓MVC框架,其实就是在Asp.Net管道上扩展的,在PostResolveCache事件扩展了UrlRoutingModule,     /// 会在任何请求进来后,先进行路由匹配,如果匹配上了,就指定HttpHandler;没有匹配就还是走原始流程     ///      /// 扩展自己的Route,写入RouteCollection,可以自定义规则完成路由     /// 扩展HttpHandle,就可以为所欲为,跳出MVC框架     /// </summary>     public class HomeController : Controller     {         public ActionResult Index()         {             return View();         }     } }

对应的 /Home/Index 视图:

@{     ViewBag.Title = "Home Page"; }  <h2>     This is Home/Index View </h2> <hr />

未进行HttpModule注册前我们先来访问下 /Home/Index ,运行结果如下所示:

ASP.NET管道处理模型(一)

下面我们自定义一个HttpModule如下所示:

using System; using System.Collections.Generic; using System.Linq; using System.Web;  namespace AspNetPipeline.Pipeline {     /// <summary>     /// 自定义HttpModule     /// </summary>     public class CustomHttpModule : IHttpModule     {         public event EventHandler CustomHttpModuleHandler;          public void Dispose()         {             Console.WriteLine("This is CustomHttpModule.Dispose");         }          /// <summary>         /// 注册动作context         /// </summary>         /// <param name="context"></param>         public void Init(HttpApplication context)         {             context.BeginRequest += (s, e) =>             {                 this.CustomHttpModuleHandler?.Invoke(context, null);             };              //为每一个事件,都注册了一个动作,向客户端输出信息             context.AcquireRequestState += (s, e) => context.Response.Write(string.Format("<h2 style='color:#00f'>来自CustomHttpModule 的处理,{0}请求到达 {1}</h2><hr>", DateTime.Now.ToString(), "AcquireRequestState        "));             context.AuthenticateRequest += (s, e) => context.Response.Write(string.Format("<h2 style='color:#00f'>来自CustomHttpModule 的处理,{0}请求到达 {1}</h2><hr>", DateTime.Now.ToString(), "AuthenticateRequest        "));             context.AuthorizeRequest += (s, e) => context.Response.Write(string.Format("<h2 style='color:#00f'>来自CustomHttpModule 的处理,{0}请求到达 {1}</h2><hr>", DateTime.Now.ToString(), "AuthorizeRequest           "));             context.BeginRequest += (s, e) => context.Response.Write(string.Format("<h2 style='color:#00f'>来自CustomHttpModule 的处理,{0}请求到达 {1}</h2><hr>", DateTime.Now.ToString(), "BeginRequest               "));             context.Disposed += (s, e) => context.Response.Write(string.Format("<h2 style='color:#00f'>来自CustomHttpModule 的处理,{0}请求到达 {1}</h2><hr>", DateTime.Now.ToString(), "Disposed                   "));             context.EndRequest += (s, e) => context.Response.Write(string.Format("<h2 style='color:#00f'>来自CustomHttpModule 的处理,{0}请求到达 {1}</h2><hr>", DateTime.Now.ToString(), "EndRequest                 "));             context.Error += (s, e) => context.Response.Write(string.Format("<h2 style='color:#00f'>来自CustomHttpModule 的处理,{0}请求到达 {1}</h2><hr>", DateTime.Now.ToString(), "Error                      "));             context.LogRequest += (s, e) => context.Response.Write(string.Format("<h2 style='color:#00f'>来自CustomHttpModule 的处理,{0}请求到达 {1}</h2><hr>", DateTime.Now.ToString(), "LogRequest                 "));             context.MapRequestHandler += (s, e) => context.Response.Write(string.Format("<h2 style='color:#00f'>来自CustomHttpModule 的处理,{0}请求到达 {1}</h2><hr>", DateTime.Now.ToString(), "MapRequestHandler          "));             context.PostAcquireRequestState += (s, e) => context.Response.Write(string.Format("<h2 style='color:#00f'>来自CustomHttpModule 的处理,{0}请求到达 {1}</h2><hr>", DateTime.Now.ToString(), "PostAcquireRequestState    "));             context.PostAuthenticateRequest += (s, e) => context.Response.Write(string.Format("<h2 style='color:#00f'>来自CustomHttpModule 的处理,{0}请求到达 {1}</h2><hr>", DateTime.Now.ToString(), "PostAuthenticateRequest    "));             context.PostAuthorizeRequest += (s, e) => context.Response.Write(string.Format("<h2 style='color:#00f'>来自CustomHttpModule 的处理,{0}请求到达 {1}</h2><hr>", DateTime.Now.ToString(), "PostAuthorizeRequest       "));             context.PostLogRequest += (s, e) => context.Response.Write(string.Format("<h2 style='color:#00f'>来自CustomHttpModule 的处理,{0}请求到达 {1}</h2><hr>", DateTime.Now.ToString(), "PostLogRequest             "));             context.PostMapRequestHandler += (s, e) => context.Response.Write(string.Format("<h2 style='color:#00f'>来自CustomHttpModule 的处理,{0}请求到达 {1}</h2><hr>", DateTime.Now.ToString(), "PostMapRequestHandler      "));             context.PostReleaseRequestState += (s, e) => context.Response.Write(string.Format("<h2 style='color:#00f'>来自CustomHttpModule 的处理,{0}请求到达 {1}</h2><hr>", DateTime.Now.ToString(), "PostReleaseRequestState    "));             context.PostRequestHandlerExecute += (s, e) => context.Response.Write(string.Format("<h2 style='color:#00f'>来自CustomHttpModule 的处理,{0}请求到达 {1}</h2><hr>", DateTime.Now.ToString(), "PostRequestHandlerExecute  "));             context.PostResolveRequestCache += (s, e) => context.Response.Write(string.Format("<h2 style='color:#00f'>来自CustomHttpModule 的处理,{0}请求到达 {1}</h2><hr>", DateTime.Now.ToString(), "PostResolveRequestCache    "));             context.PostUpdateRequestCache += (s, e) => context.Response.Write(string.Format("<h2 style='color:#00f'>来自CustomHttpModule 的处理,{0}请求到达 {1}</h2><hr>", DateTime.Now.ToString(), "PostUpdateRequestCache     "));             context.PreRequestHandlerExecute += (s, e) => context.Response.Write(string.Format("<h2 style='color:#00f'>来自CustomHttpModule 的处理,{0}请求到达 {1}</h2><hr>", DateTime.Now.ToString(), "PreRequestHandlerExecute   "));             context.PreSendRequestContent += (s, e) => context.Response.Write(string.Format("<h2 style='color:#00f'>来自CustomHttpModule 的处理,{0}请求到达 {1}</h2><hr>", DateTime.Now.ToString(), "PreSendRequestContent      "));             context.PreSendRequestHeaders += (s, e) => context.Response.Write(string.Format("<h2 style='color:#00f'>来自CustomHttpModule 的处理,{0}请求到达 {1}</h2><hr>", DateTime.Now.ToString(), "PreSendRequestHeaders      "));             context.ReleaseRequestState += (s, e) => context.Response.Write(string.Format("<h2 style='color:#00f'>来自CustomHttpModule 的处理,{0}请求到达 {1}</h2><hr>", DateTime.Now.ToString(), "ReleaseRequestState        "));             context.RequestCompleted += (s, e) => context.Response.Write(string.Format("<h2 style='color:#00f'>来自CustomHttpModule 的处理,{0}请求到达 {1}</h2><hr>", DateTime.Now.ToString(), "RequestCompleted           "));             context.ResolveRequestCache += (s, e) => context.Response.Write(string.Format("<h2 style='color:#00f'>来自CustomHttpModule 的处理,{0}请求到达 {1}</h2><hr>", DateTime.Now.ToString(), "ResolveRequestCache        "));             context.UpdateRequestCache += (s, e) => context.Response.Write(string.Format("<h2 style='color:#00f'>来自CustomHttpModule 的处理,{0}请求到达 {1}</h2><hr>", DateTime.Now.ToString(), "UpdateRequestCache         "));         }     } }

可以在IHttpModule.Init方法内部为HttpApplication事件注册动作。

然后我们需要在Web.config里面配置下这个HttpModule节点,如下所示:

<!--托管管道模式为集成时使用这个配置--> <system.webServer>   <modules>     <add name="MyCustomHttpModule" type="AspNetPipeline.Pipeline.CustomHttpModule,AspNetPipeline"/>   </modules> </system.webServer>

ASP.NET管道处理模型(一)

其中type值为【类的完整名称 + 英文逗号 + 项目名称】。

此处,我们还在CustomHttpModule里面定义了一个CustomHttpModuleHandler事件,那么我们要在哪里给这个事件注册动作呢?

可以在Global.asax里面为CustomHttpModuleHandler事件绑定动作,如下:

using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using System.Web.Optimization; using System.Web.Routing;  namespace AspNetPipeline {     public class MvcApplication : System.Web.HttpApplication     {         protected void Application_Start()         {             AreaRegistration.RegisterAllAreas();             FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);             RouteConfig.RegisterRoutes(RouteTable.Routes);             BundleConfig.RegisterBundles(BundleTable.Bundles);         }          /// <summary>         /// 为HttpModule里面的事件注册动作         /// 配置文件module名称_module里面事件名称         /// </summary>         /// <param name="sender"></param>         /// <param name="e"></param>         protected void MyCustomHttpModule_CustomHttpModuleHandler(object sender, EventArgs e)         {             HttpContext.Current.Response.Write("<h2>This is MvcApplication/MyCustomHttpModule_CustomHttpModuleHandler</h2>");         }     } }

最后我们再来访问下 /Home/Index,看下运行结果:

ASP.NET管道处理模型(一)

其中“This is Home/Index View”这句话就是由某一个具体的IHttpHandler处理器对象来处理的。

PS

1、对HttpApplication里面的事件进行动作注册的,就叫IHttpModule。

2、自定义一个HttpModule--配置文件注册--然后任何一个请求都会执行Init里面注册给HttpApplication事件的动作。

3、HttpModule里面发布一个事件CustomHttpModuleHandler,在Global.asax增加一个动作, MyCustomHttpModule_CustomHttpModuleHandler(配置文件module名称_module里面事件名称),请求响应时,该事件会被执行。

4、HttpModule是对HttpApplication里面的事件注册动作,而Global则是对HttpModule里面的事件注册动作。

 

介绍到这里,我们知道Http的任何一个请求最终一定是由某一个具体的HttpHandler来处理的,不管是成功还是失败

竟然如此,那我们能不能自定义一个HttpHandler来处理一些特殊的请求呢?答案:可以的。

例如:我们想要实现某个特定后缀(如.log后缀)的所有请求都指派给我们自定义的HttpHandler来处理,那这个要如何实现呢?

下面我们就带大家来实现这一想法,先来看下示例所涉及到的代码的目录结构:

ASP.NET管道处理模型(一)

自定义HttpHandler:

using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Web;  namespace AspNetPipeline.Pipeline {     /// <summary>     /// 自定义HttpHandler     ///      /// 我们可以从请求级出发,避开默认机制,动态响应 .log(自定义)后缀的请求     /// </summary>     public class CustomHttpHandler : IHttpHandler     {         public bool IsReusable => true;          /// <summary>         /// 处理请求         /// </summary>         /// <param name="context"></param>         public void ProcessRequest(HttpContext context)         {             context.Response.ContentType = "text/html";             context.Response.WriteFile(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Web.config"));         }     } }

在Web.config里面进行配置说明:

<!--托管管道模式为集成时使用这个配置--> <system.webServer>   <!--可以给自己留个后门,比如读个日志文件啥的-->   <handlers>     <!--这句话的意思就是.log后缀的所有请求就指派给我们的CustomHttpHandler来处理-->     <add name="ReadLog" verb="*" path="*.log" type="AspNetPipeline.Pipeline.CustomHttpHandler,AspNetPipeline"/>   </handlers>    <modules>     <add name="MyCustomHttpModule" type="AspNetPipeline.Pipeline.CustomHttpModule,AspNetPipeline"/>   </modules> </system.webServer>

ASP.NET管道处理模型(一)

此时我们去访问一下 /log.log 会发现报错了,如下所示:

ASP.NET管道处理模型(一)

这是因为此时它被MVC的路由匹配了,所以无法找到资源。

我们需要到MVC路由配置那边把它忽略掉:

using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using System.Web.Routing;  namespace AspNetPipeline {     public class RouteConfig     {         public static void RegisterRoutes(RouteCollection routes)         {             //忽略路由  正则表达式  {resource}表示变量   a.axd/xxxx   resource=a   pathInfo=xxxx             //.axd是历史原因,最开始都是WebForm,请求都是.aspx后缀,IIS根据后缀转发请求;             //MVC出现了,没有后缀,IIS6以及更早版本,打了个补丁,把MVC的请求加上个.axd的后缀,然后这种都转发到网站             //新版本的IIS已经不需要了,遇到了就直接忽略,还是走原始流程             routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); //该行框架自带的              //.log后缀的请求忽略掉,不走MVC流程,而是用我们自定义的CustomHttpHandler处理器来处理             routes.IgnoreRoute("{resource}.log/{*pathInfo}");              routes.MapRoute(                 name: "Default",                 url: "{controller}/{action}/{id}",                 defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }             );         }     } }

最后我们再来访问下 /log.log 运行结果如下所示:

ASP.NET管道处理模型(一)

可以发现此时访问正常了,我们右键查看网页源代码,会发现输出了我们想要的东西,如下所示:

ASP.NET管道处理模型(一)

之前我们在访问 .aspx 页面时可能有个错觉,感觉就是访问物理路径,然而从上面这个例子可以看出这是不对的。

它应该是由我们的配置文件来指定映射关系:后缀名与处理程序的关系(IHttpHandler---IHttpHandlerFactory) 

自定义HttpHandler处理,就是可以处理各种后缀请求,可以加入自己的逻辑。

为了加深印象,下面我们就再举个防盗链的例子:

防盗链HttpHandler:

using System; using System.Collections.Generic; using System.Linq; using System.Web;  namespace AspNetPipeline.Pipeline {     /// <summary>     /// 防盗链HttpHandler     /// </summary>     public class ImageHttpHandler : IHttpHandler     {         public bool IsReusable => true;          public void ProcessRequest(HttpContext context)         {             // 如果UrlReferrer为空,大部分都是爬虫,则显示一张默认的禁止盗链的图片             if (context.Request.UrlReferrer == null || context.Request.UrlReferrer.Host == null)             {                 context.Response.ContentType = "image/JPEG";                 context.Response.WriteFile("/Content/Image/Forbidden.jpg");             }             else             {                 // 如果UrlReferrer中不包含自己站点主机域名,则显示一张默认的禁止盗链的图片                 if (context.Request.UrlReferrer.Host.Contains("localhost"))                 {                     // 获取文件服务器端物理路径                     string fileName = context.Server.MapPath(context.Request.FilePath);                     context.Response.ContentType = "image/JPEG";                     context.Response.WriteFile(fileName);                 }                 else                 {                     context.Response.ContentType = "image/JPEG";                     context.Response.WriteFile("/Content/Image/Forbidden.jpg");                 }             }         }     } }

在Web.config里面进行配置:

<!--托管管道模式为集成时使用这个配置--> <system.webServer>   <!--可以给自己留个后门,比如读个日志文件啥的-->   <handlers>     <!--这句话的意思就是.log后缀的所有请求就指派给我们的CustomHttpHandler来处理-->     <add name="ReadLog" verb="*" path="*.log" type="AspNetPipeline.Pipeline.CustomHttpHandler,AspNetPipeline"/>      <!--防盗链处理-->     <add name="gif" path="*.gif" verb="*" type="AspNetPipeline.Pipeline.ImageHttpHandler,AspNetPipeline" />     <add name="png" path="*.png" verb="*" type="AspNetPipeline.Pipeline.ImageHttpHandler,AspNetPipeline" />     <add name="jpg" path="*.jpg" verb="*" type="AspNetPipeline.Pipeline.ImageHttpHandler,AspNetPipeline" />     <add name="jpeg" path="*.jpeg" verb="*" type="AspNetPipeline.Pipeline.ImageHttpHandler,AspNetPipeline" />   </handlers>    <modules>     <!--自定义HttpModule-->     <!--<add name="MyCustomHttpModule" type="AspNetPipeline.Pipeline.CustomHttpModule,AspNetPipeline"/>-->   </modules> </system.webServer>

ASP.NET管道处理模型(一)

PS:此处需要将自定义的CustomHttpModule这个配置节点给注释掉,因为在CustomHttpModule中注册的动作有向客户端输出字符串,这会导致图片输出异常。

图片存放路径如下所示:

ASP.NET管道处理模型(一)

访问 /content/image/scenery.jpg 运行结果如下所示:

ASP.NET管道处理模型(一)

可以发现此时返回的并不是我们访问的真实图片,而是防止盗链的图片。 

至此本文就全部介绍完了,如果觉得对您有所启发请记得点个赞哦!!! 

 

Demo源码: 

链接:https://pan.baidu.com/s/1Rb4uq0yB_iB3VsonwiCFKw  提取码:68r6

此文由博主精心撰写转载请保留此原文链接:https://www.cnblogs.com/xyh9039/p/15201368.html

版权声明:如有雷同纯属巧合,如有侵权请及时联系本人修改,谢谢!!!