ASP.NET 管道模型简析

4 篇文章 0 订阅

       我相信在第一次听到这个名词时,有的小伙伴会一脸懵,而且还有很多疑问,其实我在第一次接触这个概念时跟大家一样,就像… 这样

接下来我将以我自己的理解来讲述什么是管道模型。

  • 什么是管道模型

       首先有没有小伙伴思考过在.NET中WebForm、Mvc、Webapi 之类的框架做应用程序的开发部署之后,用户在浏览器或是利用HttpClient 输入URL地址请求之后是怎么到达我们的应用程序呢?中间又经过了哪些步骤呢?

       其实在用户输入URL之后,Http请求会经过一系列的流转,每次流转都有一些特有的处理,而管道模型就是为Http请求提供支撑和流转而抽象出的一个模型。

  • 在实际中他是如何执行的

       老规矩先上图,然后再一步一步分析,这样比较直观,其实在isapi之前还有一些只是在流程图中没有标记出来,在下面的文字叙述中我会提及,但是这并不重要。

       接下来以文字的方式来解释一下,管道模型一部分的流程图走向情况:

1.用户发起请求到达DNS,DNS会解析域名找到对应的IP及端口。

2.IIS中HttpSys监听服务接受请求。

3.HttpSys监听服务根据请求类型的后缀,将请求转发到对应的应用程序处理(isapi.dll),IIS中处理程序映射可以
  实现配置,不同的后缀将到达不同的处理程序.net的请求映射到aspnet.isapi,java或者其他的也可以配置
  对应的dll。

5.在http请求到达Isapi后会将请求转换为一个HttpWorkerRequest对象,然后把对象传入到
  HttpRuntime.ProcessRequest在函数中利用HttpApplicationFactory传入HttpContext进行构建
  HttpApplication,当然在这前几个步骤如果发生错误,那就直接返回了。

       目前系统预设对应部分已经执行完了,由于是系统固定流程这时作为开发者并不能进行升级或者是扩展,待请求到达了HttpApplication之后的才是真正的开始。

  • HttpApplication

           HttpApplication类在管道模型中充当一个很重要的角色,它定义对 ASP.NET 应用程序内所有应用程序对象公用的方法、属性和事件。此类是用户在 Global.asax 文件中定义的应用程序的基类,订阅httpApplication事件的任何 HTTP 模块**(HttpModule)**都必须实现 IHttpModule 接口

           在我们学习管道的过程中,所注重的应该是它所发布的一系列事件,它们用来处理各式各样的请求,并且每次请求都会将它们逐一执行一遍,但由于处理不尽相同,又可能都需要,所以使用了事件的模式来扩展,具体事件内容和作用,在下方引入的代码中。

        (用事件的意义:封装共性部分,将不确定部分利用事件的方式,其实说白了就是委托回调,将需要扩展的部分以委托的形式暴露出去,在后面有更多和委托相关的内容,委托对于C#,跟指针对于C一样的伟大,一样的令人膜拜)

 public class HttpApplication : IComponent,IDisposable, 
                                IHttpAsyncHandler, IHttpHandler,
                                IRequestCompletedNotifier, ISyncContext
{
       //表示处理的开始
       public event EventHandler BeginRequest;
       //验证请求,一般用来取得请求用户的信息
       public event EventHandler AuthenticateRequest;
       //已经获取请求用户的信息
       public event  PostAuthenticateRequest;
       //	授权,一般用来检查用户的请求是否获得权限
       public event  AuthorizeRequest;
       //用户请求已经得到授权
       public event PostAuthorizeRequest;
       //获取以前处理缓存的处理结果,如果以前缓存过,那么不必再进行请求的处理,直接返回缓存结果
       public event ResolveRequestCache;
       //已经完成缓存的获取操作
       public event PostResolveRequestCache;	
       //已经根据用户的请求,创建了处理请求的处理器对象
       public event PostMapRequestHandler;
       //取得请求的状态,一般用于Session
       public event AcquireRequestState;	
       //已经取得了Session
       public event PostAcquireRequestState;
       	//准备执行处理程序
       public event PreRequestHandlerExecute;
       
       //已经执行了处理程序
       public event PostRequestHandlerExecute;	
       //释放请求的状态
       public event ReleaseRequestState;	
       //已经释放了请求的状态
       public event PostReleaseRequestState;
       //更新缓存
       public event UpdateRequestCache;
       //	已经更新了缓存
       public event PostUpdateRequestCache;
       //请求的日志操作
       public event LogRequest;
       //已经完成了请求的日志操作
       public event PostLogRequest;
       本次请求处理完成
       public event EndRequest;	
}
  • HttpModule
            疑问又来了,既然HttpApplication发布了一系列事件,我想对其发布某个事件进行扩展或者自定义又该如何操作呢?接下来就该HttpModule登场了,没错他才是主角,在ASP.NET中占有举足轻重的地位,因为无论是WebForm,又或者是Mvc乃至Webapi都只是HttpModule延伸出来的一环而已,突然感觉自己很渺小,赶紧停止学习太可怕了

突然这样说可能让人无法理解,慢慢来接着往下看,一切就会慢慢清晰了

        由于每一次Http请求,都会将HttpApplication中所有的事件执行一遍,同理那实现订阅的HttpModule也会针对每一个请求而执行一次,所以比较适合做一些全局的操作,例如缓存,或者实现请求压缩,下面用一个实例来实现HttpModule请求压缩,以及实现的步骤

  • 1.首先创建一个类GzipModule 实现IHttpModule接口
internal class GzipModule : IHttpModule
    {
        public void Dispose()
        {
        }

        public void Init(HttpApplication app)
        {
            app.BeginRequest += App_BeginRequest;
        }

        private void App_BeginRequest(object sender, EventArgs e)
        {
            HttpApplication app = (HttpApplication)sender;
            string IsAcceptType = app.Context.Request.Headers["Accept-Encoding"];

            //请求头不包含格式编码直接拦截返回
            if (string.IsNullOrEmpty(IsAcceptType)) return;
            IsAcceptType = IsAcceptType.ToUpperInvariant();

            //如果客户端请求头包含压缩请求标识那么响应也返回压缩格式
            //如果是默认请求就返回默认的响应格式
            if (IsAcceptType.Contains("GZIP"))
            {
                app.Context.Response.AppendHeader("Content-encoding", "gzip");
                app.Context.Response.Filter = new GZipStream(app.Context.Response.Filter
              , CompressionMode.Compress);
            }
            else if (IsAcceptType.Contains("DEFLATE"))
            {
                app.Context.Response.AppendHeader("Content-encoding", "deflate");
                app.Context.Response.Filter = new DeflateStream(app.Context.Response.Filter
              , CompressionMode.Compress);
            }
        }
    }
  • 2.在配置文件下注册HttpModule
	<system.webServer>
		<httpErrors existingResponse="PassThrough" />
		<modules>
          <add name="GzipModule" type="namespace.GzipModule, namespace" preCondition="integratedMode" />
		</modules>
	</system.webServer>

        到这里您可能会问有没有动态注册的方式不写在配置文件,我的回答是有的,以webapi框架为例

1.在AssemblyInfo文件中加入编译特性,在代码编译时动态注册

 [assembly: PreApplicationStartMethod(typeof(DynamicRegisterHttpMoudle), "RegisterMoudle")]
 public class DynamicRegisterHttpMoudle
 {
    public static void RegisterMoudle()
    {
        HttpApplication.RegisterModule(typeof(GzipModule));
    }
 }

2.利用静态构造函数注入,因为静态构造函数是在类加载时首先被CLR调用的

    public class WebApiApplication : System.Web.HttpApplication
    {
      static WebApiApplication(){
        RegisterModule(typeof(GzipModule));
      }
      protected void Application_Start() { }
    }
  • HttpHandler

       接下来我们还要认识一个重要的内容HttpHandler,至于它是什么?相对正式的回答是这样的:

HttpHandler是一个HTTP请求的真正处理中心,也正是在这个HttpHandler容器中,ASP.NET Framework才真正地对客户端请求的服务器页面做出编译和执行并将处理过后的信息附加在HTTP请求信息流中再次返回到HttpModule中。

       不过从字面意思还是能看得懂的,至于大白话的意思就是http最终的请求是由这个叫HttpHandler来完成的,到底是不是这样呢?在后面我会进一步去验证,先来做一个HttpHandler简单的实现,我们自定义一个后缀为.cc请求的被我们自定义的HttpHandler处理。

1.首先自定义类然后继承自IHttpHandler

 public class CustomHttpHander : IHttpHandler
    {
        public bool IsReusable => false;

        public void ProcessRequest(HttpContext context)
        {
            string time =DateTime.Now.ToString();
            string respone = string.Format("自定义HttpHander,{0}请求到达", time );
            context.Response.Write(respone);
        }
    }

2.配置文件注册HttpHandler

<system.webServer>
		<httpErrors existingResponse="PassThrough" />
		<modules>
     	  <add name="CustomHttpHandler" path="*.cc" verb="*" type="namespace.CustomHttpHander,namespace" preCondition="integratedMode,runtimeVersionv4.0" />
		</modules>
	</system.webServer>

    这样我们就自定义了一个.cc后缀的请求被CustomHttpHandler处理,至于为什么能实现特殊请求后缀的自定义,这是HttpHandler的机制,但是它的核心内容还是一个Http请求处理中心,当然我们后续会进一步了解的MvcHandler 、HttpControllerHandler这些mvc框架中的核心处理程序,从源码的角度来更进一步的了解它。

     至此管道模型大致的概念以及执行流程已经在本文中做了简单分析,后续学习Mvc和Webapi的源码因为和管道这一块衔接很紧密,有了这一部分的铺垫,相对会更加容易理解。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值