Files
DocumentService/DocumentOperator.API/Middleware/ExceptionHandlingMiddleware.cs
TekH 0e88b349d7 Rebrand project: DocumentOperator to DocumentService
This commit implements a complete rebranding of the project:
- Updated all namespaces from `DocumentOperator` to `DocumentService`.
- Renamed file paths, embedded resources, and test data references.
- Updated configuration keys, logging paths, and Redis instance names.
- Revised documentation to reflect the new project name.
- Modified project and solution files to align with the new structure.
- Updated class names, DTOs, commands, queries, and handlers.
- Adjusted middleware, controllers, and API endpoints.
- Updated Swagger metadata and API titles to `DocumentService API`.
- Refactored test namespaces, resource paths, and embedded resources.
- Updated build and deployment configurations for the new name.
- Replaced all references to `DocumentOperator` in comments and literals.

These changes ensure consistency across the codebase and documentation.
2026-07-30 14:02:56 +02:00

110 lines
3.9 KiB
C#

using DocumentService.Domain.Common.Exceptions;
using FluentValidation;
using Microsoft.AspNetCore.Mvc;
using System.Net;
using System.Text.Json;
namespace DocumentService.API.Middleware;
/// <summary>
/// Central exception handling middleware
/// Maps exceptions to HTTP status codes and RFC 7807 Problem Details
/// </summary>
/// <remarks>
/// Initializes a new instance of the <see cref="ExceptionHandlingMiddleware"/> class.
/// </remarks>
/// <param name="Next">The next middleware in the pipeline.</param>
public class ExceptionHandlingMiddleware(RequestDelegate Next)
{
private static readonly JsonSerializerOptions ProbDetailsJsonOpt = new()
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
/// <summary>
/// Invokes the middleware to handle incoming HTTP requests and catch exceptions.
/// </summary>
/// <param name="context">The HTTP context for the current request.</param>
public async Task InvokeAsync(HttpContext context)
{
try
{
await Next(context);
}
catch (Exception ex)
{
await HandleExceptionAsync(context, ex);
}
}
private static async Task HandleExceptionAsync(HttpContext context, Exception exception)
{
var (statusCode, problemDetails) = MapExceptionToProblemDetails(exception, context);
context.Response.StatusCode = (int)statusCode;
context.Response.ContentType = "application/problem+json";
await context.Response.WriteAsync(JsonSerializer.Serialize(problemDetails, ProbDetailsJsonOpt));
}
private static (HttpStatusCode StatusCode, ProblemDetails ProblemDetails) MapExceptionToProblemDetails(
Exception exception,
HttpContext context)
{
return exception switch
{
// FluentValidation (400 Bad Request)
ValidationException validationEx => (
HttpStatusCode.BadRequest,
new ProblemDetails
{
Type = "https://datatracker.ietf.org/doc/html/rfc7231#section-6.5.1",
Title = "Validation Error",
Status = (int)HttpStatusCode.BadRequest,
Detail = string.Join("; ", validationEx.Errors.Select(e => e.ErrorMessage)),
Instance = context.Request.Path
}
),
// Bad Request Exception (400 Bad Request)
BadRequestException badReqEx => (
HttpStatusCode.BadRequest,
new ProblemDetails
{
Type = "https://datatracker.ietf.org/doc/html/rfc7231#section-6.5.4",
Title = "Bad Request",
Status = (int)HttpStatusCode.BadRequest,
Detail = badReqEx.Message,
Instance = context.Request.Path
}
),
// Not Found Exception (404 Not Found)
NotFoundException notFoundEx => (
HttpStatusCode.NotFound,
new ProblemDetails
{
Type = "https://datatracker.ietf.org/doc/html/rfc7231#section-6.5.4",
Title = "Resource Not Found",
Status = (int)HttpStatusCode.NotFound,
Detail = notFoundEx.Message,
Instance = context.Request.Path
}
),
// Generic Exception (500 Internal Server Error)
_ => (
HttpStatusCode.InternalServerError,
new ProblemDetails
{
Type = "https://datatracker.ietf.org/doc/html/rfc7231#section-6.6.1",
Title = "Internal Server Error",
Status = (int)HttpStatusCode.InternalServerError,
Detail = "An unexpected error occurred. Please contact support.",
Instance = context.Request.Path
}
)
};
}
}