.NET8 WebApplication剖析
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);
值得注意的是 IStartupValidator
、IHostedService
、IHostedLifecycleService
分别为我们提供不同的钩子,只需要向容器注册即可加入我们自定义的业务逻辑。
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的 StartAsync
、WaitForShutdownAsync
在上面都介绍了,WebApplication
的 DisposeAsync
也在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
异常,进而在这种情况下还能够正常处理销毁工作,这是一种非常优秀的编程习惯。