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();