mirror of
https://github.com/azaion/missions.git
synced 2026-06-21 16:51:08 +00:00
7025f4d075
Updated JWT authentication to use configuration values instead of hardcoded secrets, improving security and flexibility. Enhanced CORS policy to conditionally allow origins based on configuration settings, with logging for permissive defaults. Updated README to reflect project renaming and clarify service context.
64 lines
4.3 KiB
Markdown
64 lines
4.3 KiB
Markdown
# Module: `Azaion.Missions.Middleware`
|
|
|
|
> **NOTE (forward-looking)**: post-rename namespace. Today's source still lives under `Azaion.Flights.Middleware`. Renames tracked under Jira AZ-EPIC child B5.
|
|
|
|
**Files (1)**: `Middleware/ErrorHandlingMiddleware.cs`
|
|
|
|
## Purpose
|
|
|
|
Global exception → JSON error response mapper. Wraps the rest of the request pipeline and converts a fixed set of exception types to specific HTTP status codes; everything else becomes a 500.
|
|
|
|
## Public Interface
|
|
|
|
```csharp
|
|
public class ErrorHandlingMiddleware(RequestDelegate next, ILogger<ErrorHandlingMiddleware> logger) {
|
|
public Task Invoke(HttpContext context);
|
|
}
|
|
```
|
|
|
|
Standard ASP.NET Core middleware shape (primary-constructor variant).
|
|
|
|
## Internal Logic
|
|
|
|
```text
|
|
try { await next(context); }
|
|
catch (KeyNotFoundException ex) → 404 NotFound, body: { statusCode, message = ex.Message }
|
|
catch (ArgumentException ex) → 400 BadRequest, body: { statusCode, message = ex.Message }
|
|
catch (InvalidOperationException ex) → 409 Conflict, body: { statusCode, message = ex.Message }
|
|
catch (Exception ex) → 500 InternalServerError, body: { statusCode, message = "Internal server error" }
|
|
PLUS: logger.LogError(ex, "Unhandled exception");
|
|
```
|
|
|
|
Body is serialized via `JsonSerializer.Serialize(new { statusCode, message })` — an **anonymous object literal**, NOT the `DTOs.ErrorResponse` type. The anonymous-type property names are written lowercase-first in code (`statusCode`, `message`); `System.Text.Json` preserves them as-is when no `JsonNamingPolicy` is configured, so the wire shape is `{"statusCode":..., "message":"..."}` — **camelCase by accidental match with the suite spec**. (The unused `DTOs.ErrorResponse` type, by contrast, declares PascalCase properties and would serialize PascalCase if it were ever used directly.)
|
|
|
|
`Content-Type` is set to `application/json`. Response body is written via `WriteAsync` (string).
|
|
|
|
## Dependencies
|
|
|
|
- `System.Net` (`HttpStatusCode`)
|
|
- `System.Text.Json` (`JsonSerializer`)
|
|
- ASP.NET Core middleware abstractions (transitive via `Microsoft.NET.Sdk.Web`)
|
|
- `Microsoft.Extensions.Logging.ILogger<T>` (transitive)
|
|
|
|
No internal dependencies.
|
|
|
|
## Consumers
|
|
|
|
- `Program.cs` — `app.UseMiddleware<ErrorHandlingMiddleware>();` is called BEFORE `UseCors`, `UseAuthentication`, `UseAuthorization`, `UseSwagger`, `UseSwaggerUI`, `MapControllers`.
|
|
|
|
## Configuration / External Integrations / Security
|
|
|
|
None.
|
|
|
|
## Tests
|
|
|
|
None present.
|
|
|
|
## Notes / Smells
|
|
|
|
1. **`DTOs.ErrorResponse` is unused here** — middleware writes an anonymous object instead. **Partial spec divergence**: `../../suite/_docs/00_top_level_architecture.md` § Error Response Format mandates `{ "statusCode", "message", "errors": object? }` (camelCase, `errors` is an *object* of per-field arrays). The middleware's anonymous-object output IS already camelCase on case (`{"statusCode":..., "message":"..."}`) but missing the `errors` field; the static `ErrorResponse` DTO has the wrong shape (`List<string>? Errors` instead of `object?`) and is dead code.
|
|
2. **Entity / DTO body case-style** is PascalCase across the rest of the API (controller responses serialize entities and `PaginatedResponse<T>` via `System.Text.Json` defaults with no naming policy override). The error envelope's accidental camelCase match documented in point 1 does NOT extend to those responses — see `architecture.md` ADR-002.
|
|
3. **`InvalidOperationException` → 409 Conflict** is a non-standard mapping. The .NET BCL throws this for many "wrong state at the moment" conditions; in this codebase it is used by `VehicleService.DeleteVehicle` to report "vehicle is referenced by missions" (a true 409). But any third-party library throwing `InvalidOperationException` for an unrelated reason would also surface as 409, masking the real cause.
|
|
4. **Generic 500 swallows the message** (good for security — no internal detail leaked) and logs the exception (good for diagnostics). No correlation ID is included in the response, so production support has to grep logs by timestamp.
|
|
5. **Order of catches** matters: `ArgumentException` is base of `ArgumentNullException` / `ArgumentOutOfRangeException`, so those also become 400 (correct). `InvalidOperationException` ahead of generic `Exception` is correct.
|