ASP.NET Core HTTP 管道中的那些事儿

 

前言

马上2016年就要过去了,时间可是真快啊。

上次写完 Identity 系列之后,反响还不错,所以本来打算写一个 ASP.NET Core 中间件系列的,但是中间遇到了很多事情。首先是 NPOI 的移植工作,移植过后还有一些Bug需要修复,然后一个事情是一个有关于分布式架构中消息一致性的一个中间件设计开发工作,还有就是博客还要坚持写,最后就是还有好几本书需要读,以及消化学习,所以深深感觉到时间不够用。废话不多说了,进入主题。

大部分中间件都是要依赖于 HTTP 管道和 HttpAbstractions 提供的上下文的,所以有必要先介绍一下 ASP.NET Core 在管道中封装了什么,了解清楚之后有助于更高的进阶学习。你看不懂也没有关系,对这些概念有一个认识就可以了,不需要你精通每一步都具体做了哪些东西,达到会用就是我的目的。因为我觉得随着你以后开发的深入,对这些东西使用多了之后,自然会想了解更多,到那个时候,你就会去看源码了。

IApplicationBuilder

IApplicationBuilder 是应用大家最熟悉它的地方应该就是位于 Startup.cs 文件中的 Configure 方法了吧

public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
{
app.UseDeveloperExceptionPage();
app.UseStaticFiles();
app.UseMvc();
}

IApplicationBuilder 就像其名字一样,其实很简单,就是对内做了一个接口封装,对外提供了一个扩展,具体来说就是

对内封装了

  • IServiceProvider ApplicationServices { get; set; }:应用程序使用的服务,就是ConfigureServices注入的那些。
  • IFeatureCollection ServerFeatures { get; }: 工具箱对象,下面会提到的。
  • IDictionary<string, object> Properties { get; } :横跨各个中间件,在他们之间共享数据。

对外扩展了

  • IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware):注册中间件

还有一个 Build 用来返回管道最终的 HttpContext 状态

  • RequestDelegate Build(); : 最终的 RequestDelegate 形态

没什么复杂的东西,就这几个东西需要了解,下面是一个 IApplicationBuilder 的贴图,看起来应该会更直观。

那 RequestDelegate 又是什么东西呢?

RequestDelegate

RequestDelegate 是一个委托,是以委托形式对 HttpContext 的封装,在 ASP.NET Core 中也是非常重要的一个对象,先看一下定义吧。

namespace Microsoft.AspNetCore.Http
{
public delegate Task RequestDelegate(HttpContext context);
}

为什么说这个对象很重要呢,因为在管道中所有的中间件都是直接或者间接的使用到它来做一些功能,因为它包装这 HttpContext 的嘛,而在Web开发中 HttpContext 的地位又是如此的高,接下来就说说 HttpContext

HttpContext

HttpContext 应该是做Web开发最熟悉的一个对象了,在这个对象中,封装着Http整个管道中的关键对象信息,这个对象同时伴随着整个管道中传输,也就是会经历整个请求的生命周期,所以这个对象非常重要,也是学习Web开发的一个基础知识。

与传统 .NET Framework 中的 System.Web.HttpContext 不同的是,在 ASP.NET Core 中 HttpContext 是一个抽象类,位于 Microsoft.AspNetCore.Http 命名空间下。它有一个默认的实现叫 DefaultHttpContext 位于 Microsoft.AspNetCore.Http 程序集。

我们知道以前的 System.Web.HttpContext 是怎么创建的吧?

在传统ASP.NET程序中,IIS接收到请求之后,会交给 aspnet_isapi.dll 这个程序集,程序集启动 ASP.NET 运行环境,然后调用 ISAPIRuntime 进行封装,封装之后就是一个 HttpWorkRequest 对象了,然后由 HttpRuntime将其转化为一个HttpContext。

所以在System.Web.HttpContext中,其构造函数为 HttpWorkerRequest

namespace System.Web
{
public sealed class HttpContext : IServiceProvider, IPrincipalContainer
{
public HttpContext(HttpWorkerRequest wr);
}
}

也就是说其实构建HttpContext所有的信息都是来自于 IIS 传递下来的 HttpWorkerRequest

现在,忘了它吧,是的全部忘掉,这个时候你又要骂微软了,尼玛当年为了面试这个东西不知道背了多少遍。 =_=

没办法,时代要发展要进步,我们也需要不断的学习,那在 ASP.NET Core 中,没有了 IIS ,它的 HttpContext 又是如何生成的呢? 构建 HttpContext 所需的信息又怎么来的呢? 客官不要着急,听我慢慢分解

IFeatureCollection

这里就需要先说一下另外一个比较重要接口了,它就是 IFeatureCollection 接口,这个接口是干嘛的呢? 我们先看一下它的定义:

public interface IFeatureCollection : IEnumerable<KeyValuePair<Type, object>>
{ bool IsReadOnly { get; } int Revision { get; } object this[Type key] { get; set; } TFeature Get<TFeature>(); void Set<TFeature>(TFeature instance);
}

看到这个接口的定义应该能够明白其功能了,接口继承于IEnumerable<KeyValuePair<Type, object>> 说明接口是一个键值对的集合,我们给接口取个名字吧,叫“工具箱”。

有些同学应该猜出来了,是的,ASP.NET Core中构建 HttpContext 就是所有需要的组件都是来自于 工具箱,那么 工具箱 里面都有什么呢?可以看到工具箱里面有 IHttpRequestFeature, IHttpResponseFeature, ISessionFeature 等等还有很多就不一一列举了,正是由于这一个一个的工具箱里面的工具,最终构建成为了整个 HttpContext 对象。我们来看一下 HttpContext 对象的构造函数:

namespace Microsoft.AspNetCore.Http
{
public class DefaultHttpContext : HttpContext
{
public DefaultHttpContext(IFeatureCollection features)
{
Initialize(features);
}
}
}

没错吧,有同学可能要问了,工具箱里面的这些工具什么时候初始化的呢? 先别急,我们先看看我们熟悉的 Request 和 Response。

HttpRequest

同样在 ASP.NET Core ,HttpRequest 也成为了一个抽象类,它的默认实现叫 DefaultHttpRequest,主要是封装浏览器对服务器的请求的各种数据,包括浏览器请求的网址,查询字符串数据或表单数据等等...

不一一详细介绍了,都是很简单的内容。

HttpResponse

HttpResponse 也成为了一个抽象类,它的默认实现叫 DefaultHttpResponse,主要是是服务器接收到浏览器的请求后,处理返回结果常用的一个对象。

ASP.NET Core 管道

是时候该解释一下上面的 工具箱 初始化的问题了,我想我用一个图来直接说明管道中的请求再合适不过了,下面就是一个 ASP.NET Core 整个服务端构建管道一个流程图:

从图中可以看到,RequestDelegate 携带着 HttpContext 一路经过各种 Server、Hosting 等,最终到达了由 IApplicationBuilder 构建出来的 Application pipeline 这一管道区域,然后再经过各种中间件处理,最终构建出来了我们的 Response,而我们的工具箱 也正是在这个过程中变得 “ 饱满 ” 起来。

有一个需要知道的知识点就是,中间件是怎么样添加或者叫注册到管道中的呢? 又是如何被应用起来的呢?

上面的图可以看到,橙色区域的 Application pipeline 是由 IApplicationBuilder 构建起来的。也就是说我们可以在 IApplicationBuilder 做点什么东西来添加我们的中间件。 是的 IApplicationBuilder 暴露出来了一个 IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware); 方法来让我们注册中间件,也就是说位于 Startup.cs 文件中的 Configure 方法。

那么又是怎么样应用起来的呢? IApplicationBuilder 在 Hosting 中有一个 IApplicationBuilderFactory 的对象,Hosting 通过这个Factory 创建之后就会传递到了 HostingApplication 对象中,最后由 IWebHost 对象调用 IServer 对象的Start 同时把 HostingApplication传递进去来最终启动服务端。可以对照上面的图理解一下。

总结

在本篇中,我们知道了 ASP.NET Core Http 管道中的几个对象,他们分别是 IApplicationBuilder , RequestDelegate , HttpContext , HttpRequest , HttpResponseIFeatureCollection 等,然后也知道了他们之间的关系,接着我们通过一个管道流程图知道了他们的调用关系,也知道了如何向管道中注册中间件。

NET Core HTTP 管道的更多相关文章

  1. ASP.NET Core HTTP 管道中的那些事儿

    前言 马上2016年就要过去了,时间可是真快啊. 上次写完 Identity 系列之后,反响还不错,所以本来打算写一个 ASP.NET Core 中间件系列的,但是中间遇到了很多事情.首先是 NPOI ...

  2. 用.Net Core控制台模拟一个ASP.Net Core的管道模型

    在我的上几篇文章中降到了asp.net core的管道模型,为了更清楚地理解asp.net core的管道,再网上学习了.Net Core控制台应用程序对其的模拟,以加深映像,同时,供大家学习参考. ...

  3. .net core 中间件管道底层剖析

    .net core 管道(Pipeline)是什么? 由上图可以看出,.net core 管道是请求抵达服务器到响应结果返回的中间的一系列的处理过程,如果我们简化一下成下图来看的话,.net core ...

  4. asp.net core mvc 管道之中间件

    asp.net core mvc 管道之中间件 http请求处理管道通过注册中间件来实现各种功能,松耦合并且很灵活 此文简单介绍asp.net core mvc中间件的注册以及运行过程 通过理解中间件 ...

  5. net core 中间件管道

    net core 中间件管道 .net core 管道(Pipeline)是什么? 由上图可以看出,.net core 管道是请求抵达服务器到响应结果返回的中间的一系列的处理过程,如果我们简化一下成下 ...

  6. 如果你想深刻理解ASP.NET Core请求处理管道,可以试着写一个自定义的Server

    我们在上面对ASP.NET Core默认提供的具有跨平台能力的KestrelServer进行了详细介绍(<聊聊ASP.NET Core默认提供的这个跨平台的服务器——KestrelServer& ...

  7. ASP.NET Core真实管道详解[2]:Server是如何完成针对请求的监听、接收与响应的【上】

    Server是ASP .NET Core管道的第一个节点,负责完整请求的监听和接收,最终对请求的响应同样也由它完成.Server是我们对所有实现了IServer接口的所有类型以及对应对象的统称,如下面 ...

  8. ASP.NET Core真实管道详解[1]:中间件是个什么东西?

    ASP.NET Core管道虽然在结构组成上显得非常简单,但是在具体实现上却涉及到太多的对象,所以我们在 <ASP.NET Core管道深度剖析[共4篇]> 中围绕着一个经过极度简化的模拟 ...

  9. ASP.NET Core真实管道详解[1]

    ASP.NET Core管道虽然在结构组成上显得非常简单,但是在具体实现上却涉及到太多的对象,所以我们在 <ASP.NET Core管道深度剖析[共4篇]> 中围绕着一个经过极度简化的模拟 ...

随机推荐

  1. IOS block 循环引用的解决

    在介绍block循环引用前我们先了解一下typeof. typeof是什么??? typeof 是一个一元运算,放在一个运算数之前,运算数可以是任意类型. 它返回值是一个字符串,该字符串说明运算数的类 ...

  2. Unity3D 面试题汇总

    最先执行的方法是: 1.(激活时的初始化代码)Awake,2.Start.3.Update[FixUpdate.LateUpdate].4.(渲染模块)OnGUI.5.再向后,就是卸载模块(TearD ...

  3. 如何退出调起多个Activity的Application?

    1.记录打开的Activity 每打开一个activity,即记录下来,需要关闭时,关闭每一个activity即可. 2.发送特定的广播 在需要结束应用时,发送一个特定广播,每个activity收到此 ...

  4. composer 学习资料

    今天看了一下composer,前几天开始用包依赖,以前一直都是自己手动配.今天用了composer,要学习的话可以按照以下链接学习: 1 官方文档: http://www.phpcomposer.co ...

  5. json与jsonp的区别

    前言 由于Sencha Touch 2这种开发模式的特性,基本决定了它原生的数据交互行为几乎只能通过AJAX来实现. 当然了,通过调用强大的PhoneGap插件然后打包,你可以实现100%的Socke ...

  6. Linux下python安装升级详细步骤 | Python2 升级 Python3

    Linux下python升级步骤  Python2 ->Python3 多数情况下,系统自动的Python版本是2.x 或者yum直接安装的也是2.x 但是,现在多数情况下建议使用3.x 那么如 ...

  7. SQLServer:删除log文件和清空日志的方法

    数据库的性能是DBA都需要重点关注的,日志文件的增多严重影响数据库的性能,本文将为您介绍SQL Server删除日志文件的方法,供您参考,希望对您有所帮助. 数据库在使用过程中会使日志文件不断增加,使 ...

  8. C语言 时间函数的学习和总结

    一直都是以简单的time_t t,time(&t),ctime(&t)来表示时间,后来要以时间为日志文件的名字时,就有点蒙逼了.学习一下.   tm结构: struct tm {   ...

  9. x01.os.13: 文件系统

    停了两天电,忽然得空闲.找来破吉他,已然不成弦.           丁丁当当敲,敲到电来到.为把时间捡,熬夜三四点. 从我的置顶随笔 x01.Lab.Download 中下载 x01.os.12.t ...

  10. java 正则表达式的应用:读取文件,获取其中的电话号码

    1.正则表达式 正则表达式,又称正规表示法.常规表示法(英语:Regular Expression,在代码中常简写为regex.regexp或RE),计算机科学的一个概念.正则表达式使用单个字符串来描 ...