diff --git a/CHANGELOG.md b/CHANGELOG.md index 777620c..57bb830 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -54,6 +54,8 @@ This project uses famous football stadiums (A-Z) that hosted FIFA World Cup matc ### Changed +- Call `.DisableRateLimiting()` on `MapHealthChecks("/health")` to enforce the exemption from the global rate limiter at the endpoint level, ensuring health check probes are never throttled (#451) +- Move `UseCors()` before `MapControllers()` in `Program.cs` to follow the standard ASP.NET Core middleware pipeline order; add an `Infrastructure` service registration section separating cross-cutting concerns (health checks, CORS, rate limiting, Swagger) from the `Controllers` section; add descriptive phrases to top-level section banners; add inline comments explaining the purpose and ordering rationale of each middleware; document the dev-only CORS policy intent in both `Program.cs` and `ServiceCollectionExtensions.AddCorsDefaultPolicy` (#451) - Replace pre-seeded `storage/players-sqlite3.db` binary blob with EF Core `MigrateAsync()` at startup: schema and seed data are now applied automatically before the first request is served; `STORAGE_PATH` env var controls the database file path (Docker volume path in production, `AppContext.BaseDirectory/storage/` locally); the committed database file, `Dockerfile` db copy step, and `scripts/run-migrations-and-copy-database.sh` have been removed (#459) - Recreate EF Core migrations using `HasData()` in `OnModelCreating`: three self-contained migrations (`InitialCreate` DDL, `SeedStarting11` DML, `SeedSubstitutes` DML) generated by EF Core with literal `InsertData` values — no migration calls application methods; `NormalizePlayerDataset` patch migration eliminated by folding corrections into seed data from the start (#459) - Replace `DatabaseFakes.CreateTable()` (placeholder schema) and `DatabaseFakes.Seed()` (manual insert bypassing migrations) with `DatabaseFakes.MigrateAsync()`, which applies the full EF Core migration chain on in-memory SQLite (#459) diff --git a/src/Dotnet.Samples.AspNetCore.WebApi/Extensions/ServiceCollectionExtensions.cs b/src/Dotnet.Samples.AspNetCore.WebApi/Extensions/ServiceCollectionExtensions.cs index 800c81b..75c13d5 100644 --- a/src/Dotnet.Samples.AspNetCore.WebApi/Extensions/ServiceCollectionExtensions.cs +++ b/src/Dotnet.Samples.AspNetCore.WebApi/Extensions/ServiceCollectionExtensions.cs @@ -54,10 +54,21 @@ IWebHostEnvironment environment } /// - /// Adds a default CORS policy that allows any origin, method, and header. + /// Adds a default CORS policy that allows any origin, method, and header, + /// restricted to the Development environment. ///
/// ///
+ /// + /// The permissive wildcard policy (AllowAnyOrigin, AllowAnyMethod, AllowAnyHeader) + /// is intentional for local development, where Swagger UI and local frontends + /// need unrestricted cross-origin access. No CORS policy is registered in + /// Production or other environments, where the API is assumed to be consumed + /// server-to-server or to sit behind a reverse proxy on the same origin, making + /// CORS irrelevant. If a production frontend on a different domain is ever added, + /// replace this with a restrictive named policy that enumerates specific allowed + /// origins instead of using a wildcard. + /// /// The IServiceCollection instance. /// The web host environment. /// The IServiceCollection for method chaining. diff --git a/src/Dotnet.Samples.AspNetCore.WebApi/Program.cs b/src/Dotnet.Samples.AspNetCore.WebApi/Program.cs index d4c04bf..363778b 100644 --- a/src/Dotnet.Samples.AspNetCore.WebApi/Program.cs +++ b/src/Dotnet.Samples.AspNetCore.WebApi/Program.cs @@ -5,6 +5,10 @@ /* ----------------------------------------------------------------------------- * Web Application + * Registers all services into the DI container before the application is built. + * Throughout this section, builder.Services refers to the ASP.NET Core + * dependency injection (DI) container — not to be confused with the Services + * subsection below, which registers our own application-level business logic. * https://learn.microsoft.com/en-us/aspnet/core/fundamentals/startup * -------------------------------------------------------------------------- */ @@ -22,11 +26,9 @@ Log.Logger = new LoggerConfiguration().ReadFrom.Configuration(builder.Configuration).CreateLogger(); builder.Host.UseSerilog(); -/* Controllers -------------------------------------------------------------- */ +/* Infrastructure ----------------------------------------------------------- */ builder.Services.AddHealthChecks(); -builder.Services.AddControllers(); -builder.Services.AddValidators(); builder.Services.AddCorsDefaultPolicy(builder.Environment); builder.Services.AddFixedWindowRateLimiter(builder.Configuration); @@ -35,7 +37,12 @@ builder.Services.AddSwaggerConfiguration(builder.Configuration); } -/* Services ----------------------------------------------------------------- */ +/* Controllers -------------------------------------------------------------- */ + +builder.Services.AddControllers(); +builder.Services.AddValidators(); + +/* Services (Business Logic) ------------------------------------------------ */ builder.Services.RegisterPlayerService(); builder.Services.AddMemoryCache(); @@ -53,6 +60,7 @@ /* ----------------------------------------------------------------------------- * Database Migration + * Applies pending EF Core migrations at startup, before the app accepts requests. * https://learn.microsoft.com/en-us/ef/core/managing-schemas/migrations/applying#apply-migrations-at-runtime * -------------------------------------------------------------------------- */ @@ -64,21 +72,53 @@ /* ----------------------------------------------------------------------------- * Middlewares + * Defines the order in which middleware components process each HTTP request. * https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware + * https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware#middleware-order * -------------------------------------------------------------------------- */ +// Replaces the default ASP.NET Core request logging with Serilog's structured +// logging, emitting one log entry per request with timing, status code, and +// other contextual properties. app.UseSerilogRequestLogging(); + +// Custom middleware that catches unhandled exceptions and returns a consistent +// RFC 7807 Problem Details response instead of exposing a raw stack trace. app.UseExceptionHandling(); + +// Redirects all plain HTTP requests to HTTPS, enforcing transport security. app.UseHttpsRedirection(); -app.MapHealthChecks("/health"); + +// DisableRateLimiting() exempts the health check endpoint from the global rate +// limiter so that monitoring and orchestration systems can always assess +// liveness and readiness without being throttled. +app.MapHealthChecks("/health").DisableRateLimiting(); + +// Enforces the fixed-window rate limiting policy defined during service +// registration, returning 429 Too Many Requests when the limit is exceeded. app.UseRateLimiter(); -app.MapControllers(); if (app.Environment.IsDevelopment()) { + // Only active in Development, where AddCorsDefaultPolicy registers a + // permissive wildcard policy for Swagger UI and local frontends. No policy + // exists in Production — the API is assumed to be consumed server-to-server + // or behind a reverse proxy on the same origin, where CORS is not needed. + // Must precede MapControllers so CORS headers are applied before any + // endpoint executes, consistent with the standard middleware pipeline order. app.UseCors(); + + // Generates the OpenAPI JSON document consumed by Swagger UI. app.UseSwagger(); + + // Serves the interactive Swagger UI at /swagger, allowing manual + // exploration and testing of the API endpoints during development. app.UseSwaggerUI(); } +// Routes incoming HTTP requests to the matching controller actions. Must come +// after all middleware that needs to run before endpoint execution (CORS, rate +// limiting, etc.). +app.MapControllers(); + await app.RunAsync();