摘要:在上面的 IServer 定义里有一个 requestHandler 的 对象,在 asp.net core 里是一个名称为  RequestDelegate 的对象,而用来构建这个委托的在 asp.net core 里是  IApplicationBuilder ,这些在蒋老师和 Edison 的文章和代码里都可以看到,这里我们只是简单介绍下,我在 MiniAspNetCore 的示例中没有使用这些对象,而是使用了自己抽象的  PipelineBuilder 和原始委托实现的。asp.net core 里 RequestDelegate 定义:。

动手写一个简版 asp.net core

Intro

之前看到过蒋金楠老师的一篇 200 行代码带你了解 asp.net core 框架,最近参考蒋老师和 Edison 的文章和代码,结合自己对 asp.net core 的理解 ,最近自己写了一个 MiniAspNetCore ,写篇文章总结一下。

HttpContext

HttpContext 可能是最为常用的一个类了,  HttpContext 是请求上下文,包含了所有的请求信息以及响应信息,以及一些自定义的用于在不同中间件中传输数据的信息

来看一下 HttpContext 的定义:

HttpRequest 即为请求信息对象,包含了所有请求相关的信息,

HttpResponse 为响应信息对象,包含了请求对应的响应信息

RequestServices 为 asp.net core 里的  RequestServices ,代表当前请求的服务提供者,可以使用它来获取具体的服务实例

Features 为 asp.net core 里引入的对象,可以用来在不同中间件中传递信息和用来解耦合

,下面我们就来看下 HttpRequest 和  HttpResponse 是怎么实现的

HttpRequest:

HttpResponse:

Features

上面我们提供我们可以使用 Features 在不同中间件中传递信息和解耦合

由上面 HttpRequestHttpResponse 的代码我们可以看出来,  HttpRequest 和  HttpResponse 其实就是在  IRequestFeature 和  IResponseFeature 的基础上封装了一层,真正的核心其实是  IRequestFeatureIResponseFeature ,而这里使用接口就很好的实现了解耦,可以根据不同的 WebServer 使用不同的  RequestFeatureResponseFeature ,来看下  IRequestFeatureIResponseFeature 的实现

这里的实现和 asp.net core 的实际的实现方式应该不同,asp.net core 里 Headers 同一个 Header 允许有多个值,asp.net core 里是 StringValues 来实现的,这里简单处理了,使用了一个 NameValueCollection 对象

上面提到的 Features 是一个  IFeatureCollection 对象,相当于是一系列的  Feature 对象组成的,来看下  FeatureCollection 的定义:

这里 IFeatureCollection 直接实现  IDictionary<Type,object> ,通过一个字典 Feature 类型为 Key,Feature 对象为 Value 的字典来保存

为了方便使用,可以定义两个扩展方法来方便的Get/Set

Web服务器

上面我们已经提到了 Web 服务器通过 IRequestFeatureIResponseFeature 来实现不同 web 服务器和应用程序的解耦,web 服务器只需要提供自己的  RequestFeatureResponseFeature 即可

为了抽象不同的 Web 服务器,我们需要定义一个 IServer 的抽象接口,定义如下:

IServer 定义了一个  StartAsync 方法,用来启动 Web服务器,

StartAsync 方法有两个参数,一个是 requestHandler,是一个用来处理请求的委托,另一个是取消令牌用来停止 web 服务器

示例使用了 HttpListener 来实现了一个简单 Web 服务器,  HttpListenerServer 定义如下:

HttpListenerServer 实现的  RequestFeatureResponseFeatue

为了方便使用,为 HttpListenerContext 定义了两个扩展方法,就是上面  HttpListenerServer 中的  GetRequestFeatureGetResponseFeature

RequestDelegate

在上面的 IServer 定义里有一个 requestHandler 的 对象,在 asp.net core 里是一个名称为  RequestDelegate 的对象,而用来构建这个委托的在 asp.net core 里是  IApplicationBuilder ,这些在蒋老师和 Edison 的文章和代码里都可以看到,这里我们只是简单介绍下,我在 MiniAspNetCore 的示例中没有使用这些对象,而是使用了自己抽象的  PipelineBuilder 和原始委托实现的

asp.net core 里 RequestDelegate 定义:

其实和我们上面定义用的 Func<HttpContext,Task> 是等价的

IApplicationBuilder 定义:

我们这里没有定义 IApplicationBuilder ,使用了简化抽象的  IAsyncPipelineBuilder ,定义如下:

对于 asp.net core 的中间件来说 ,上面的 TContext 就是  HttpContext ,替换之后也就是下面这样的:

是不是和 IApplicationBuilder 很像,如果不像可以进一步把  Func<HttpContext,Task> 使用  RequestDelegate 替换

最后再将接口名称替换一下:

至此,就完全可以看出来了,这 IAsyncPipelineBuilder<HttpContext> 就是一个简版的  IApplicationBuilder

IAsyncPipelineBuilder 和  IApplicationBuilder 的作用是将注册的多个中间件构建成一个请求处理的委托

中间件处理流程:

更多关于 PipelineBuilder 构建中间件的信息可以查看 让 .NET 轻松构建中间件模式代码 了解更多

WebHost

通过除了 Web 服务器之外,还有一个 Web Host 的概念,可以简单的这样理解,一个 Web 服务器上可以有多个 Web Host,就像 IIS/nginx (Web Server) 可以 host 多个站点

可以说 WebHost 离我们的应用更近,所以我们还需要 IHost 来托管应用

WebHost 定义:

为了方便的构建 Host 对象,引入了  HostBuilder 来方便的构建一个  Host ,定义如下:

WebHostBuilder

这里的示例我在 IHostBuilder 里增加了一个  Initialize 的方法来做一些初始化的操作,我觉得有些数据初始化配置初始化等操作应该在这里操作,而不应该在  Startup 的  Configure 方法里处理,这样  Configure 方法可以更纯粹一些,只配置 asp.net core 的请求管道,这纯属个人意见,没有对错之分

这里 Host 的实现和 asp.net core 的实现不同,有需要的可以深究源码,在 asp.net core 2.x 的版本里是有一个 IWebHost 的,在 asp.net core 3.x 以及 .net 5 里是没有  IWebHost 的取而代之的是通用主机  IHost , 通过实现了一个  IHostedService 来实现  WebHost

Run

运行示例代码:

在示例项目目录下执行 dotnet run ,并访问  http://localhost:5100/ :

仔细观察浏览器 console 或  network 的话,会发现还有一个请求,浏览器会默认请求  /favicon.ico 获取网站的图标

因为我们针对这个请求没有任何中间件的处理,所以直接返回了 404

在访问 /test ,可以看到和刚才的输出完全不同,因为这个请求走了另外一个分支,相当于 asp.net core 里  MapMapWhen 的效果,另外  Run 代表里中间件的中断,不会执行后续的中间件

More

上面的实现只是我在尝试写一个简版的 asp.net core 框架时的实现,和 asp.net core 的实现并不完全一样,如果需要请参考源码,上面的实现仅供参考,上面实现的源码可以在 Github 上获取 https://github.com/WeihanLi/SamplesInPractice/tree/master/MiniAspNetCore

asp.net core 源码:https://github.com/dotnet/aspnetcore

Reference

相关文章