| | | 1 | | using ArturRios.Util.WebApi.Middleware; |
| | | 2 | | using Microsoft.AspNetCore.Builder; |
| | | 3 | | using Microsoft.Extensions.DependencyInjection; |
| | | 4 | | |
| | | 5 | | namespace ArturRios.Util.WebApi.Configuration; |
| | | 6 | | |
| | | 7 | | /// <summary> |
| | | 8 | | /// Base class that bootstraps an ASP.NET Core web API through one standard sequence. A derived class only registers |
| | | 9 | | /// its own services in <see cref="ConfigureServices"/>; the rest runs in this order: |
| | | 10 | | /// <list type="number"> |
| | | 11 | | /// <item><see cref="WebApplicationBuilderExtensions.LoadConfiguration"/>, per the command-line <see cref="Parameters"/> |
| | | 12 | | /// <item>Controllers, the invalid-model-state envelope (<see cref="ServiceCollectionExtensions.AddInvalidModelStateEnve |
| | | 13 | | /// and Swagger (<see cref="WebApplicationBuilderExtensions.AddWebApiSwagger"/>), as tuned by <see cref="WebApiStartupOp |
| | | 14 | | /// <item><see cref="ConfigureServices"/>.</item> |
| | | 15 | | /// <item>The pipeline: <see cref="ApplicationBuilderExtensions.UseStandardMiddlewares"/> (tracing, exceptions, Swagger, |
| | | 16 | | /// CORS, authentication), the extra <see cref="WebApiStartupOptions.Middlewares"/>, then the controllers.</item> |
| | | 17 | | /// </list> |
| | | 18 | | /// <see cref="Build"/> returns the <see cref="WebApplication"/>, so endpoints beyond the controllers (health checks, |
| | | 19 | | /// minimal APIs) are mapped on it before it runs. |
| | | 20 | | /// </summary> |
| | | 21 | | public abstract class WebApiStartup |
| | | 22 | | { |
| | | 23 | | private bool _built; |
| | | 24 | | |
| | | 25 | | /// <summary>Parses <paramref name="args"/> and creates the builder, using the standard options.</summary> |
| | | 26 | | /// <param name="args">The command-line arguments passed to the application entry point.</param> |
| | 0 | 27 | | protected WebApiStartup(string[] args) : this(args, _ => { }) { } |
| | | 28 | | |
| | | 29 | | /// <summary>Parses <paramref name="args"/> and creates the builder, tuning the standard sequence with |
| | | 30 | | /// <paramref name="configure"/>.</summary> |
| | | 31 | | /// <param name="args">The command-line arguments passed to the application entry point.</param> |
| | | 32 | | /// <param name="configure">Tunes Swagger, CORS, the invalid-model-state envelope and the extra middlewares.</param> |
| | 20 | 33 | | protected WebApiStartup(string[] args, Action<WebApiStartupOptions> configure) |
| | | 34 | | { |
| | 20 | 35 | | ArgumentNullException.ThrowIfNull(configure); |
| | | 36 | | |
| | 20 | 37 | | Parameters = new WebApiParameters(args); |
| | 20 | 38 | | Builder = WebApplication.CreateBuilder(Parameters.ToWebApplicationOptions()); |
| | | 39 | | |
| | 20 | 40 | | configure(Options); |
| | 20 | 41 | | } |
| | | 42 | | |
| | | 43 | | /// <summary>The startup parameters parsed from the command-line arguments.</summary> |
| | 61 | 44 | | protected WebApiParameters Parameters { get; } |
| | | 45 | | |
| | | 46 | | /// <summary>The options tuning the standard sequence.</summary> |
| | 152 | 47 | | protected WebApiStartupOptions Options { get; } = new(); |
| | | 48 | | |
| | | 49 | | /// <summary>The builder the application is built from. Its environment is |
| | | 50 | | /// <see cref="WebApiParameters.EnvironmentName"/> when one was supplied.</summary> |
| | 139 | 51 | | protected WebApplicationBuilder Builder { get; } |
| | | 52 | | |
| | | 53 | | /// <summary>Registers the application's own services: data access, handlers, token authentication, CORS, hosted |
| | | 54 | | /// services, and so on. Runs after the standard services and before the application is built.</summary> |
| | | 55 | | /// <param name="builder">The builder, exposing <c>Services</c>, <c>Configuration</c> and <c>Environment</c>.</param |
| | | 56 | | protected abstract void ConfigureServices(WebApplicationBuilder builder); |
| | | 57 | | |
| | | 58 | | /// <summary>Runs the standard sequence and returns the built application, ready to have further endpoints |
| | | 59 | | /// mapped and to run.</summary> |
| | | 60 | | /// <exception cref="InvalidOperationException">The application was already built.</exception> |
| | | 61 | | public WebApplication Build() |
| | | 62 | | { |
| | 21 | 63 | | if (_built) |
| | | 64 | | { |
| | 1 | 65 | | throw new InvalidOperationException("The application was already built; Build can be called only once."); |
| | | 66 | | } |
| | | 67 | | |
| | 20 | 68 | | _built = true; |
| | | 69 | | |
| | 20 | 70 | | Builder.LoadConfiguration(Parameters); |
| | | 71 | | |
| | 20 | 72 | | Builder.Services.AddControllers(); |
| | | 73 | | |
| | 20 | 74 | | if (Options.UseInvalidModelStateEnvelope) |
| | | 75 | | { |
| | 19 | 76 | | Builder.Services.AddInvalidModelStateEnvelope(); |
| | | 77 | | } |
| | | 78 | | |
| | 20 | 79 | | Builder.AddWebApiSwaggerWith(Parameters, Options.Swagger); |
| | | 80 | | |
| | 20 | 81 | | Builder.Services.Configure<TraceActivityOptions>(trace => |
| | 20 | 82 | | { |
| | 16 | 83 | | trace.LogClientIp = Options.TraceActivity.LogClientIp; |
| | 16 | 84 | | trace.TagClientAddress = Options.TraceActivity.TagClientAddress; |
| | 36 | 85 | | }); |
| | | 86 | | |
| | 20 | 87 | | ConfigureServices(Builder); |
| | | 88 | | |
| | 20 | 89 | | var app = Builder.Build(); |
| | | 90 | | |
| | 20 | 91 | | app.UseStandardMiddlewares(Options.CorsPolicy); |
| | 20 | 92 | | app.UseWebApiMiddlewares(Options.Middlewares); |
| | 19 | 93 | | app.MapControllers(); |
| | | 94 | | |
| | 19 | 95 | | return app; |
| | | 96 | | } |
| | | 97 | | |
| | | 98 | | /// <summary>Builds the application with <see cref="Build"/> and runs it, blocking until it shuts down.</summary> |
| | 0 | 99 | | public void Run() => Build().Run(); |
| | | 100 | | |
| | | 101 | | /// <summary>Builds the application with <see cref="Build"/> and runs it until it shuts down.</summary> |
| | 0 | 102 | | public Task RunAsync() => Build().RunAsync(); |
| | | 103 | | } |