Add missing XML doc comments to resolve CS1591 warnings: - SerilogConfiguration: Class comment - SwaggerConfiguration: Class and AddSwaggerDocumentation() method - ExceptionHandlingMiddleware: Constructor and InvokeAsync() method - RequestLoggingMiddleware: Placeholder class comment - TenantResolutionMiddleware: Placeholder class comment - Program: Partial class comment for integration test access Result: 0 CS1591 warnings in DocumentOperator.API project
148 lines
5.5 KiB
C#
148 lines
5.5 KiB
C#
using DocumentOperator.Domain.Common.Exceptions;
|
|
using DocumentOperator.Domain.Exceptions;
|
|
using FluentValidation;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using System.Net;
|
|
using System.Text.Json;
|
|
|
|
namespace DocumentOperator.API.Middleware;
|
|
|
|
/// <summary>
|
|
/// Central exception handling middleware
|
|
/// Maps exceptions to HTTP status codes and RFC 7807 Problem Details
|
|
/// </summary>
|
|
public class ExceptionHandlingMiddleware
|
|
{
|
|
private readonly RequestDelegate _next;
|
|
private readonly ILogger<ExceptionHandlingMiddleware> _logger;
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="ExceptionHandlingMiddleware"/> class.
|
|
/// </summary>
|
|
/// <param name="next">The next middleware in the pipeline.</param>
|
|
/// <param name="logger">The logger instance for exception logging.</param>
|
|
public ExceptionHandlingMiddleware(RequestDelegate next, ILogger<ExceptionHandlingMiddleware> logger)
|
|
{
|
|
_next = next;
|
|
_logger = logger;
|
|
}
|
|
|
|
/// <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)
|
|
{
|
|
_logger.LogError(ex, "Unhandled exception: {ExceptionMessage}", ex.Message);
|
|
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";
|
|
|
|
var options = new JsonSerializerOptions
|
|
{
|
|
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
|
|
};
|
|
|
|
await context.Response.WriteAsync(JsonSerializer.Serialize(problemDetails, options));
|
|
}
|
|
|
|
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
|
|
}
|
|
),
|
|
|
|
// Domain Validation Exception (400 Bad Request)
|
|
DomainValidationException domainEx => (
|
|
HttpStatusCode.BadRequest,
|
|
new ProblemDetails
|
|
{
|
|
Type = "https://datatracker.ietf.org/doc/html/rfc7231#section-6.5.1",
|
|
Title = "Domain Validation Error",
|
|
Status = (int)HttpStatusCode.BadRequest,
|
|
Detail = domainEx.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
|
|
}
|
|
),
|
|
|
|
// Swiss QR Code Not Found Exception (404 Not Found)
|
|
SwissQrCodeNotFoundException qrNotFoundEx => (
|
|
HttpStatusCode.NotFound,
|
|
new ProblemDetails
|
|
{
|
|
Type = "https://datatracker.ietf.org/doc/html/rfc7231#section-6.5.4",
|
|
Title = "Swiss QR Code Not Found",
|
|
Status = (int)HttpStatusCode.NotFound,
|
|
Detail = qrNotFoundEx.Message,
|
|
Instance = context.Request.Path
|
|
}
|
|
),
|
|
|
|
// PDF Processing Exception (500 Internal Server Error)
|
|
PdfProcessingException pdfEx => (
|
|
HttpStatusCode.InternalServerError,
|
|
new ProblemDetails
|
|
{
|
|
Type = "https://datatracker.ietf.org/doc/html/rfc7231#section-6.6.1",
|
|
Title = "PDF Processing Error",
|
|
Status = (int)HttpStatusCode.InternalServerError,
|
|
Detail = pdfEx.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
|
|
}
|
|
)
|
|
};
|
|
}
|
|
}
|