WebApplication 是用于配置HTTP管道和路由的web应用程序,接来下我将一一拆解它的组成。

/// <summary>
/// The web application used to configure the HTTP pipeline, and routes.
/// </summary>
[DebuggerDisplay("{DebuggerToString(),nq}")]
[DebuggerTypeProxy(typeof (WebApplication.WebApplicationDebugView))]
public sealed class WebApplication : IHost,IDisposable,IApplicationBuilder,IEndpointRouteBuilder,IAsyncDisposable

IHost

​ 首先Web应用是一个程序,而 IHost 就是程序的抽象

public interface IHost : IDisposable
{
IServiceProvider Services { get; }
Task StartAsync(CancellationToken cancellationToken = default (CancellationToken));
Task StopAsync(CancellationToken cancellationToken = default (CancellationToken));
}

​ 一个程序具备启动、停止生命周期,这很好理解。我要说的是 IServiceProvider ,他非常关键,后面会在依赖注入章节来详细解释。目前你只需要知道他是一个服务供应商就可以了,就可以通过他获取想要的服务,但前提是你在IOC容器中注册过。

​ Host StartAsync 代码流如下:

await host._hostLifetime.WaitForStartAsync(token1).ConfigureAwait(false); // 注册start程序
host.Services.GetService<IStartupValidator>()?.Validate(); // 校验
IHostedLifecycleService.StartingAsync
IHostedService.StartAsync
IHostedLifecycleService.StartedAsync
host._applicationLifetime.NotifyStarted();

StopAsync 类似,代码流如下:

IHostedLifecycleService.StoppingAsync
IHostedService.StopAsync
IHostedLifecycleService.StoppedAsync
this._logger.StoppedWithException((Exception) ex);

​ 值得注意的是 IStartupValidatorIHostedServiceIHostedLifecycleService 分别为我们提供不同的钩子,只需要向容器注册即可加入我们自定义的业务逻辑。

IApplicationBuilder

WebApplication 实现 IApplicationBuilder 具有pipeline机制。

IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware);
RequestDelegate Build();

​ 这里要解释一下pipeline,管道是.NET中非常普及的一个概念,内核是切面编程,同样的在后续我们会有专门的章节来例举它。现在你只需要知道,他是一个洋葱模型。

​ 同时,IApplicationBuilder 从命名上就表达了这是一个构建者模式,因此 WebApplication 提供了 Build

public WebApplication Build()
{
this._hostApplicationBuilder.Services.Add(this._genericWebHostServiceDescriptor);
this.Host.ApplyServiceProviderFactory(this._hostApplicationBuilder);
this._builtApplication = new WebApplication(this._hostApplicationBuilder.Build());
return this._builtApplication;
}

​ 篇幅问题这里不展开讨论,但在Build方法中会有四个钩子被执行

public IHostBuilder ConfigureHostConfiguration(Action<IConfigurationBuilder> configureDelegate)
public IHostBuilder ConfigureAppConfiguration(Action<HostBuilderContext, IConfigurationBuilder> configureDelegate)
public IHostBuilder ConfigureServices(Action<HostBuilderContext, IServiceCollection> configureDelegate)
public IHostBuilder ConfigureContainer<TContainerBuilder>(Action<HostBuilderContext, TContainerBuilder> configureDelegate)

​ 最终服务容器会被标记成只读

IEndpointRouteBuilder

IEndpointRouteBuilder为程序定义路由构建的约定。

ICollection<EndpointDataSource> DataSources { get; }

​ 这是 WebApplication 中的实现,可以看到 EndpointDataSource 的实现会被组合进来。

public IReadOnlyList<Endpoint> Endpoints
{
get
{
EndpointDataSource requiredService = this._webApplication.Services.GetRequiredService<EndpointDataSource>();
return requiredService is CompositeEndpointDataSource endpointDataSource && endpointDataSource.DataSources.Intersect<EndpointDataSource>((IEnumerable<EndpointDataSource>) this._webApplication.DataSources).Count<EndpointDataSource>() != this._webApplication.DataSources.Count ? new CompositeEndpointDataSource((IEnumerable<EndpointDataSource>) this._webApplication.DataSources).Endpoints : requiredService.Endpoints;
}
}

EndpointDataSource实际上就是一组 Endpoint,而 Endpoint 是 AspNetCore 下极其重要的一节,同样会在后续展开讲。现在你只需要知道,它表示一个处理 HTTP 请求的终点,包含了处理请求的逻辑和相关的元数据。

IAsyncDisposable

IAsyncDisposable 是 .NET Core 2.0 引入的一个接口,用于异步释放资源的模式。它是 IDisposable 接口的异步版本。某些资源的释放可能涉及到异步操作,使用 IDisposable 接口的同步释放模式可能会导致阻塞线程,影响应用程序的性能和响应性。

public interface IAsyncDisposable
{
ValueTask DisposeAsync();
}

​ 在使用完该对象后,可以使用 await using语法糖或直接调用 ``DisposeAsync()` 方法来释放资源。

Run

Run 函数是 WebApplication 的启动按钮,你可以传递一个url,加入到监听列表

public void Run([StringSyntax("Uri")] string? url = null)
{
this.Listen(url);
// public static void Run(this IHost host) => host.RunAsync().GetAwaiter().GetResult();
HostingAbstractionsHostExtensions.Run(this);
}

HostingAbstractionsHostExtensions.Run的源码相对简单,host的 StartAsyncWaitForShutdownAsync 在上面都介绍了,WebApplicationDisposeAsync 也在finally块中触发

public static async Task RunAsync(this IHost host, CancellationToken token = default (CancellationToken))
{
try
{
ConfiguredTaskAwaitable configuredTaskAwaitable = host.StartAsync(token).ConfigureAwait(false);
await configuredTaskAwaitable;
configuredTaskAwaitable = host.WaitForShutdownAsync(token).ConfigureAwait(false);
await configuredTaskAwaitable;
}
finally
{
if (host is IAsyncDisposable asyncDisposable)
await asyncDisposable.DisposeAsync().ConfigureAwait(false);
else
host.Dispose();
}
}

​ 上面有个很有意思的点是,为什么要在finally块中触发?这是因为Host的生命周期函数都用了联合取消令牌,这是一种安全取消协作模式,在令牌取消后会触发一个 OperationCanceledException 异常,进而在这种情况下还能够正常处理销毁工作,这是一种非常优秀的编程习惯。

.NET8 WebApplication剖析的更多相关文章

  1. 探索C#之6.0语法糖剖析

    阅读目录: 自动属性默认初始化 自动只读属性默认初始化 表达式为主体的函数 表达式为主体的属性(赋值) 静态类导入 Null条件运算符 字符串格式化 索引初始化 异常过滤器when catch和fin ...

  2. jQuery之Deferred源码剖析

    一.前言 大约在夏季,我们谈过ES6的Promise(详见here),其实在ES6前jQuery早就有了Promise,也就是我们所知道的Deferred对象,宗旨当然也和ES6的Promise一样, ...

  3. [C#] 剖析 AssemblyInfo.cs - 了解常用的特性 Attribute

    剖析 AssemblyInfo.cs - 了解常用的特性 Attribute [博主]反骨仔 [原文]http://www.cnblogs.com/liqingwen/p/5944391.html 序 ...

  4. Membership三步曲之进阶篇 - 深入剖析Provider Model

    Membership 三步曲之进阶篇 - 深入剖析Provider Model 本文的目标是让每一个人都知道Provider Model 是什么,并且能灵活的在自己的项目中使用它. Membershi ...

  5. 《AngularJS深度剖析与最佳实践》简介

    由于年末将至,前阵子一直忙于工作的事务,不得已暂停了微信订阅号的更新,我将会在后续的时间里尽快的继续为大家推送更多的博文.毕竟一个人的力量微薄,精力有限,希望大家能理解,仍然能一如既往的关注和支持sh ...

  6. 探索c#之Async、Await剖析

    阅读目录: 基本介绍 基本原理剖析 内部实现剖析 重点注意的地方 总结 基本介绍 Async.Await是net4.x新增的异步编程方式,其目的是为了简化异步程序编写,和之前APM方式简单对比如下. ...

  7. ASP.NET Core管道深度剖析(2):创建一个“迷你版”的管道来模拟真实管道请求处理流程

    从<ASP.NET Core管道深度剖析(1):采用管道处理HTTP请求>我们知道ASP.NET Core请求处理管道由一个服务器和一组有序的中间件组成,所以从总体设计来讲是非常简单的,但 ...

  8. [C#] 走进异步编程的世界 - 剖析异步方法(上)

    走进异步编程的世界 - 剖析异步方法(上) 序 这是上篇<走进异步编程的世界 - 开始接触 async/await 异步编程>(入门)的第二章内容,主要是与大家共同深入探讨下异步方法. 本 ...

  9. [C#] 走进异步编程的世界 - 剖析异步方法(下)

    走进异步编程的世界 - 剖析异步方法(下) 序 感谢大家的支持,这是昨天发布<走进异步编程的世界 - 剖析异步方法(上)>的补充篇. 目录 异常处理 在调用方法中同步等待任务 在异步方法中 ...

  10. 计算机程序的思维逻辑 (29) - 剖析String

    上节介绍了单个字符的封装类Character,本节介绍字符串类.字符串操作大概是计算机程序中最常见的操作了,Java中表示字符串的类是String,本节就来详细介绍String. 字符串的基本使用是比 ...

随机推荐

  1. 2021-7-8 VUE的简易表单提交

    <!DOCTYPE html> <html> <head> <title> </title> <style type="te ...

  2. git报错:SSL certificate problem: unable to get local issuer certificate

    原因:在windows系统中git没有获取到ssl证书 解决方案 输入以下命令: git config --global http.sslBackend schannel 之后再执行操作就可以啦 另: ...

  3. Kitex微服务开发实践(ETCD服务注册)

    服务注册通常用于分布式系统或微服务架构中,是一种用于管理和发现这些分布式服务的机制.它的目标是让服务能够动态地找到其他服务,并能够与其进行通信,而无需显式地配置其位置信息 本文简单讲述使用etcd进行 ...

  4. 《深入理解Java虚拟机》读书笔记:HotSpot虚拟机对象探秘

    基于实用优先的原则,以常用的虚拟机HotSpot和常用的内存区域Java堆为例,深入探讨HotSpot虚拟机在Java堆中对象分配.布局和访问的全过程.以下是本节内容的脑图. HotSpot虚拟机对象 ...

  5. 连续下雨天,.net开发者如何预防流感

    最近连续下了3天雨,天气变化大,很容易引发感冒咳嗽等疾病.对于.NET技术开发人员来说,如何保持身体健康,保证工作效率是一个很重要的问题. 首先,我们需要注意保持室内空气流通,避免长时间处于封闭的空间 ...

  6. STM32中SWD下载不进去的解决方法

    这是我第一次写自己的博客,希望以后写博客可以当做自己的个人习惯并坚持下去,作为技术分享,也欢迎各位大佬前来指正.本人本科学习的机械电子工程,了解机械制图.嵌入式编程.目前刚好学习了PCB制板,正在向着 ...

  7. github.com/yuin/gopher-lua 踩坑日记

    本文主要记录下在日常开发过程中, 使用 github.com/yuin/gopher-lua 过程中需要注意的地方. 后续遇到其他的需要注意的事项再补充. 1.加载LUA_PATH环境变量 在实际开发 ...

  8. 图解Spark Graphx基于connectedComponents函数实现连通图底层原理

    原创/朱季谦 第一次写这么长的graphx源码解读,还是比较晦涩,有较多不足之处,争取改进. 一.连通图说明 连通图是指图中的任意两个顶点之间都存在路径相连而组成的一个子图. 用一个图来说明,例如,下 ...

  9. 1.15 自实现GetProcAddress

    在正常情况下,要想使用GetProcAddress函数,需要首先调用LoadLibraryA函数获取到kernel32.dll动态链接库的内存地址,接着在调用GetProcAddress函数时传入模块 ...

  10. 拯救“消失的她”——双系统grub完美恢复方案

    双系统grub意外消失怎么办? 不用重装系统.不用去维修店.不会丢数据,教你一招,完美恢复grub! 背景 我的电脑是windows和linux双系统,启动项使用的grub.某天准备切换linux时突 ...