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