Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,21 @@ IWebHostEnvironment environment
}

/// <summary>
/// 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.
/// <br />
/// <see href="https://learn.microsoft.com/en-us/aspnet/core/security/cors"/>
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
/// <param name="services">The IServiceCollection instance.</param>
/// <param name="environment">The web host environment.</param>
/// <returns>The IServiceCollection for method chaining.</returns>
Expand Down
52 changes: 46 additions & 6 deletions src/Dotnet.Samples.AspNetCore.WebApi/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
* -------------------------------------------------------------------------- */

Expand All @@ -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);

Expand All @@ -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();
Expand All @@ -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
* -------------------------------------------------------------------------- */

Expand All @@ -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();
Loading