.NET8 WebApplication剖析

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

c# 复制代码
/// <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 就是程序的抽象

c# 复制代码
public interface IHost : IDisposable
{
  IServiceProvider Services { get; }
  Task StartAsync(CancellationToken cancellationToken = default (CancellationToken));
  Task StopAsync(CancellationToken cancellationToken = default (CancellationToken));
}

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

​ Host StartAsync 代码流如下:

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

StopAsync 类似,代码流如下:

c# 复制代码
IHostedLifecycleService.StoppingAsync
IHostedService.StopAsync
IHostedLifecycleService.StoppedAsync
this._logger.StoppedWithException((Exception) ex);

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

IApplicationBuilder

WebApplication 实现 IApplicationBuilder 具有pipeline机制。

c# 复制代码
IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware);
RequestDelegate Build();

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

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

c# 复制代码
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方法中会有四个钩子被执行

c# 复制代码
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为程序定义路由构建的约定。

c# 复制代码
ICollection<EndpointDataSource> DataSources { get; }

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

c# 复制代码
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 接口的同步释放模式可能会导致阻塞线程,影响应用程序的性能和响应性。

c# 复制代码
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块中触发

c# 复制代码
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 异常,进而在这种情况下还能够正常处理销毁工作,这是一种非常优秀的编程习惯。

相关推荐
小乖兽技术1 个月前
详解Asp.Net Core管道模型中的五种过滤器的适用场景与用法
后端·asp.net core·管道机制
界面开发小八哥1 个月前
界面控件Telerik UI for ASP.NET Core 2024 Q2亮点 - AI与UI的融合
人工智能·ui·asp.net·asp.net core·telerik
饭勺oO2 个月前
Elsa V3学习之Flowchart详解(上)
c#·asp.net core·.net core·工作流·elsa
饭勺oO2 个月前
Elsa V3学习之脚本
c#·asp.net core·.net core·工作流·elsa
饭勺oO2 个月前
Elsa V3学习之Hello Word
c#·asp.net core·.net core·工作流·elsa
绿荫阿广2 个月前
使用Aspire优雅的进行全栈开发——WinUI使用Semantic Kernel调用智普清言LLM总结Asp.Net Core通过Playwright解析的网页内容
asp.net core·winui·semantic kernel
代数狂人2 个月前
【Chapter 4 .NET 8.0 ASP.NET Core BookQuerySystem: Project Layout】
后端·asp.net·.net·asp.net core
coredx3 个月前
基于EF Core存储的国际化服务
c#·asp.net core
yangshuquan4 个月前
分享 ASP.NET Core Web Api 中间件获取 Request Body 两个方法
中间件·c#·asp.net core·编程经验
饭勺oO4 个月前
还在拼冗长的WhereIf吗?100行代码解放这个操作
c#·asp.net core·.net core