PharmaVault includes a global exception handling middleware that intercepts all unhandled exceptions before they reach the HTTP response pipeline. Every exception — whether anticipated or unexpected — is logged to theDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/JReyna217/PharmaVault/llms.txt
Use this file to discover all available pages before exploring further.
error_logs table in PostgreSQL with a UUID incident number generated by the database, and a structured JSON error response is returned to the caller. This means no exception silently disappears, and every production incident can be traced to a specific database record.
ExceptionHandlingMiddleware
The middleware is registered inProgram.cs via app.UseMiddleware<ExceptionHandlingMiddleware>(), placing it early in the request pipeline so that exceptions thrown by authentication, authorisation, routing, and Blazor component rendering are all captured.
HandleExceptionAsync, which classifies the exception into one of two paths:
- Controlled Exception (ErrorExceptionResponse)
- Unhandled Exception
If the thrown exception is an
ErrorExceptionResponse, the middleware treats it as an expected, developer-defined error. It uses the status code and error code carried by the exception directly, logs the pre-populated Details object, and emits a LogWarning entry.ErrorExceptionResponse
ErrorExceptionResponse extends Exception and gives service and DAO code a structured way to raise an error with a specific HTTP status code, a machine-readable error code string, and a fully populated log payload — all in a single throw.
ErrorExceptionResponse with the desired HTTP status code and a fully described ExceptionLogDto:
404 response with the ErrorMessage as the error field in the JSON body.
ExceptionLogDto
ExceptionLogDto is the payload that travels from the throw site to the middleware and then into the database. For controlled exceptions the caller populates it explicitly; for unhandled exceptions the middleware populates it automatically from the exception’s reflection metadata.
| Field | Purpose |
|---|---|
OriginLayer | The architectural layer where the error originated, e.g. "Service", "Data", or "Backend". |
MainObject | The class name responsible for the error, e.g. "InventoryDao". |
MethodName | The specific method where the error occurred. |
ErrorMessage | A concise, human-readable description of what went wrong. This value becomes the error field in the JSON response for controlled exceptions. |
Description | Optional extended detail — used to store the stack trace for unhandled exceptions, or a contextual explanation for controlled ones. |
Error Response Format
Every response produced by the middleware follows the same JSON shape, regardless of whether the exception was controlled or unhandled:error will contain the ErrorMessage from the ExceptionLogDto, and status will reflect the HttpStatusCode passed to ErrorExceptionResponse. The incidentId is always a UUID corresponding to the row written to error_logs.
error_logs Table
Every exception — controlled or unhandled — is persisted to theerror_logs table in PostgreSQL. The incident_number column is a UUID generated by PostgreSQL’s gen_random_uuid() function at insert time via a RETURNING incident_number clause, so the UUID is created atomically with the row and returned to the middleware for inclusion in the response.
The ErrorLog model reflects the full table schema:
IErrorLogDao
TheIErrorLogDao interface exposes a single method used by the middleware:
LogErrorAsync accepts the log payload and an optional user ID, inserts the row, and returns the PostgreSQL-generated incident_number UUID. The concrete implementation (ErrorLogDao) uses Npgsql and raw SQL:
User Context
The middleware attempts to resolve the authenticated user’s ID before logging. It reads theClaimTypes.NameIdentifier claim from the current HttpContext.User and, if present and parseable as an integer, passes it to LogErrorAsync as userId. If the request is unauthenticated or the claim is absent, userId is null — the error is still logged, just without a user association.
error_logs that was produced during an authenticated session carries a user_id, making it straightforward to identify which user encountered a specific class of error.
For Blazor Server apps, unhandled exceptions that occur inside Razor component rendering are also caught by the
GlobalErrorBoundary Razor component. The error boundary displays an in-page error message to the user without a full page reload. The HTTP middleware and the error boundary complement each other — the middleware covers API and pipeline-level exceptions while the error boundary handles interactive component exceptions.