/
WebApplication.cs
216 lines (181 loc) · 8.47 KB
/
WebApplication.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Hosting.Server.Features;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace Microsoft.AspNetCore.Builder;
/// <summary>
/// The web application used to configure the HTTP pipeline, and routes.
/// </summary>
public sealed class WebApplication : IHost, IApplicationBuilder, IEndpointRouteBuilder, IAsyncDisposable
{
internal const string GlobalEndpointRouteBuilderKey = "__GlobalEndpointRouteBuilder";
private readonly IHost _host;
private readonly List<EndpointDataSource> _dataSources = new();
internal WebApplication(IHost host)
{
_host = host;
ApplicationBuilder = new ApplicationBuilder(host.Services, ServerFeatures);
Logger = host.Services.GetRequiredService<ILoggerFactory>().CreateLogger(Environment.ApplicationName ?? nameof(WebApplication));
Properties[GlobalEndpointRouteBuilderKey] = this;
}
/// <summary>
/// The application's configured services.
/// </summary>
public IServiceProvider Services => _host.Services;
/// <summary>
/// The application's configured <see cref="IConfiguration"/>.
/// </summary>
public IConfiguration Configuration => _host.Services.GetRequiredService<IConfiguration>();
/// <summary>
/// The application's configured <see cref="IWebHostEnvironment"/>.
/// </summary>
public IWebHostEnvironment Environment => _host.Services.GetRequiredService<IWebHostEnvironment>();
/// <summary>
/// Allows consumers to be notified of application lifetime events.
/// </summary>
public IHostApplicationLifetime Lifetime => _host.Services.GetRequiredService<IHostApplicationLifetime>();
/// <summary>
/// The default logger for the application.
/// </summary>
public ILogger Logger { get; }
/// <summary>
/// The list of URLs that the HTTP server is bound to.
/// </summary>
public ICollection<string> Urls => ServerFeatures.GetRequiredFeature<IServerAddressesFeature>().Addresses;
IServiceProvider IApplicationBuilder.ApplicationServices
{
get => ApplicationBuilder.ApplicationServices;
set => ApplicationBuilder.ApplicationServices = value;
}
internal IFeatureCollection ServerFeatures => _host.Services.GetRequiredService<IServer>().Features;
IFeatureCollection IApplicationBuilder.ServerFeatures => ServerFeatures;
internal IDictionary<string, object?> Properties => ApplicationBuilder.Properties;
IDictionary<string, object?> IApplicationBuilder.Properties => Properties;
internal ICollection<EndpointDataSource> DataSources => _dataSources;
ICollection<EndpointDataSource> IEndpointRouteBuilder.DataSources => DataSources;
internal ApplicationBuilder ApplicationBuilder { get; }
IServiceProvider IEndpointRouteBuilder.ServiceProvider => Services;
/// <summary>
/// Initializes a new instance of the <see cref="WebApplication"/> class with preconfigured defaults.
/// </summary>
/// <param name="args">Command line arguments</param>
/// <returns>The <see cref="WebApplication"/>.</returns>
public static WebApplication Create(string[]? args = null) =>
new WebApplicationBuilder(new() { Args = args }).Build();
/// <summary>
/// Initializes a new instance of the <see cref="WebApplicationBuilder"/> class with preconfigured defaults.
/// </summary>
/// <returns>The <see cref="WebApplicationBuilder"/>.</returns>
public static WebApplicationBuilder CreateBuilder() =>
new(new());
/// <summary>
/// Initializes a new instance of the <see cref="WebApplicationBuilder"/> class with preconfigured defaults.
/// </summary>
/// <param name="args">Command line arguments</param>
/// <returns>The <see cref="WebApplicationBuilder"/>.</returns>
public static WebApplicationBuilder CreateBuilder(string[] args) =>
new(new() { Args = args });
/// <summary>
/// Initializes a new instance of the <see cref="WebApplicationBuilder"/> class with preconfigured defaults.
/// </summary>
/// <param name="options">The <see cref="WebApplicationOptions"/> to configure the <see cref="WebApplicationBuilder"/>.</param>
/// <returns>The <see cref="WebApplicationBuilder"/>.</returns>
public static WebApplicationBuilder CreateBuilder(WebApplicationOptions options) =>
new(options);
/// <summary>
/// Start the application.
/// </summary>
/// <param name="cancellationToken"></param>
/// <returns>
/// A <see cref="Task"/> that represents the startup of the <see cref="WebApplication"/>.
/// Successful completion indicates the HTTP server is ready to accept new requests.
/// </returns>
public Task StartAsync(CancellationToken cancellationToken = default) =>
_host.StartAsync(cancellationToken);
/// <summary>
/// Shuts down the application.
/// </summary>
/// <param name="cancellationToken"></param>
/// <returns>
/// A <see cref="Task"/> that represents the shutdown of the <see cref="WebApplication"/>.
/// Successful completion indicates that all the HTTP server has stopped.
/// </returns>
public Task StopAsync(CancellationToken cancellationToken = default) =>
_host.StopAsync(cancellationToken);
/// <summary>
/// Runs an application and returns a Task that only completes when the token is triggered or shutdown is triggered.
/// </summary>
/// <param name="url">The URL to listen to if the server hasn't been configured directly.</param>
/// <returns>
/// A <see cref="Task"/> that represents the entire runtime of the <see cref="WebApplication"/> from startup to shutdown.
/// </returns>
public Task RunAsync(string? url = null)
{
Listen(url);
return HostingAbstractionsHostExtensions.RunAsync(this);
}
/// <summary>
/// Runs an application and block the calling thread until host shutdown.
/// </summary>
/// <param name="url">The URL to listen to if the server hasn't been configured directly.</param>
public void Run(string? url = null)
{
Listen(url);
HostingAbstractionsHostExtensions.Run(this);
}
/// <summary>
/// Disposes the application.
/// </summary>
void IDisposable.Dispose() => _host.Dispose();
/// <summary>
/// Disposes the application.
/// </summary>
public ValueTask DisposeAsync() => ((IAsyncDisposable)_host).DisposeAsync();
internal RequestDelegate BuildRequestDelegate() => ApplicationBuilder.Build();
RequestDelegate IApplicationBuilder.Build() => BuildRequestDelegate();
// REVIEW: Should this be wrapping another type?
IApplicationBuilder IApplicationBuilder.New()
{
var newBuilder = ApplicationBuilder.New();
// Remove the route builder so branched pipelines have their own routing world
newBuilder.Properties.Remove(GlobalEndpointRouteBuilderKey);
return newBuilder;
}
/// <summary>
/// Adds the middleware to the application request pipeline.
/// </summary>
/// <param name="middleware">The middleware.</param>
/// <returns>An instance of <see cref="IApplicationBuilder"/> after the operation has completed.</returns>
public IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware)
{
ApplicationBuilder.Use(middleware);
return this;
}
IApplicationBuilder IEndpointRouteBuilder.CreateApplicationBuilder() => ((IApplicationBuilder)this).New();
private void Listen(string? url)
{
if (url is null)
{
return;
}
var addresses = ServerFeatures.Get<IServerAddressesFeature>()?.Addresses;
if (addresses is null)
{
throw new InvalidOperationException($"Changing the URL is not supported because no valid {nameof(IServerAddressesFeature)} was found.");
}
if (addresses.IsReadOnly)
{
throw new InvalidOperationException($"Changing the URL is not supported because {nameof(IServerAddressesFeature.Addresses)} {nameof(ICollection<string>.IsReadOnly)}.");
}
addresses.Clear();
addresses.Add(url);
}
}