2024-05-28 07:16:15 +03:00
|
|
|
|
using FluentValidation;
|
|
|
|
|
using Microsoft.AspNetCore.Http;
|
2024-12-22 05:25:19 +03:00
|
|
|
|
using Microsoft.AspNetCore.Mvc;
|
2024-10-27 03:02:25 +03:00
|
|
|
|
using Microsoft.Extensions.Logging;
|
2024-05-28 07:16:15 +03:00
|
|
|
|
using Mirea.Api.DataAccess.Application.Common.Exceptions;
|
|
|
|
|
using Mirea.Api.Endpoint.Common.Exceptions;
|
|
|
|
|
using System;
|
2024-12-22 05:25:19 +03:00
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Diagnostics;
|
|
|
|
|
using System.Linq;
|
2024-10-31 04:07:35 +03:00
|
|
|
|
using System.Security;
|
2024-05-28 07:16:15 +03:00
|
|
|
|
using System.Text.Json;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
|
2024-10-07 02:25:36 +03:00
|
|
|
|
namespace Mirea.Api.Endpoint.Configuration.Core.Middleware;
|
2024-05-28 07:16:15 +03:00
|
|
|
|
|
2024-10-27 03:02:25 +03:00
|
|
|
|
public class CustomExceptionHandlerMiddleware(RequestDelegate next, ILogger<CustomExceptionHandlerMiddleware> logger)
|
2024-05-28 07:16:15 +03:00
|
|
|
|
{
|
2024-08-12 21:36:07 +03:00
|
|
|
|
public async Task InvokeAsync(HttpContext context)
|
2024-05-28 07:16:15 +03:00
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
await next(context);
|
|
|
|
|
}
|
|
|
|
|
catch (Exception exception)
|
|
|
|
|
{
|
|
|
|
|
await HandleExceptionAsync(context, exception);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-27 03:02:25 +03:00
|
|
|
|
private Task HandleExceptionAsync(HttpContext context, Exception exception)
|
2024-05-28 07:16:15 +03:00
|
|
|
|
{
|
2024-12-22 05:25:19 +03:00
|
|
|
|
var traceId = Activity.Current?.Id ?? context.TraceIdentifier;
|
|
|
|
|
|
|
|
|
|
var problemDetails = new ProblemDetails
|
|
|
|
|
{
|
|
|
|
|
Type = "https://tools.ietf.org/html/rfc9110#section-15.6",
|
|
|
|
|
Title = "An unexpected error occurred.",
|
|
|
|
|
Status = StatusCodes.Status500InternalServerError,
|
|
|
|
|
Detail = exception.Message,
|
|
|
|
|
Extensions = new Dictionary<string, object?>()
|
|
|
|
|
{
|
|
|
|
|
{ "traceId", traceId }
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2024-05-28 07:16:15 +03:00
|
|
|
|
switch (exception)
|
|
|
|
|
{
|
|
|
|
|
case ValidationException validationException:
|
2024-12-22 05:25:19 +03:00
|
|
|
|
problemDetails.Status = StatusCodes.Status400BadRequest;
|
|
|
|
|
problemDetails.Type = "https://tools.ietf.org/html/rfc9110#section-15.5.1";
|
|
|
|
|
problemDetails.Title = "Validation errors occurred.";
|
|
|
|
|
problemDetails.Extensions = new Dictionary<string, object?>
|
2024-05-28 07:16:15 +03:00
|
|
|
|
{
|
2024-12-22 05:25:19 +03:00
|
|
|
|
{ "errors", validationException.Errors.Select(e => e.ErrorMessage).ToArray() },
|
|
|
|
|
{ "traceId", traceId }
|
|
|
|
|
};
|
2024-05-28 07:16:15 +03:00
|
|
|
|
break;
|
|
|
|
|
case NotFoundException:
|
2024-12-22 05:25:19 +03:00
|
|
|
|
problemDetails.Status = StatusCodes.Status404NotFound;
|
|
|
|
|
problemDetails.Type = "https://tools.ietf.org/html/rfc9110#section-15.5.4";
|
|
|
|
|
problemDetails.Title = "Resource not found.";
|
2024-05-28 07:16:15 +03:00
|
|
|
|
break;
|
|
|
|
|
case ControllerArgumentException:
|
2024-12-22 05:25:19 +03:00
|
|
|
|
problemDetails.Status = StatusCodes.Status400BadRequest;
|
|
|
|
|
problemDetails.Type = "https://tools.ietf.org/html/rfc9110#section-15.5.1";
|
|
|
|
|
problemDetails.Title = "Invalid arguments provided.";
|
2024-05-28 07:16:15 +03:00
|
|
|
|
break;
|
2024-10-31 04:07:35 +03:00
|
|
|
|
case SecurityException:
|
2024-12-22 05:25:19 +03:00
|
|
|
|
problemDetails.Status = StatusCodes.Status401Unauthorized;
|
|
|
|
|
problemDetails.Type = "https://tools.ietf.org/html/rfc9110#section-15.5.2";
|
|
|
|
|
problemDetails.Title = "Unauthorized access.";
|
2024-10-31 04:07:35 +03:00
|
|
|
|
break;
|
2024-12-22 05:51:11 +03:00
|
|
|
|
case ServerUnavailableException unavailableException:
|
|
|
|
|
problemDetails.Status = StatusCodes.Status503ServiceUnavailable;
|
|
|
|
|
problemDetails.Type = "https://datatracker.ietf.org/doc/html/rfc9110#section-15.6.4";
|
|
|
|
|
problemDetails.Title = "Server unavailable.";
|
|
|
|
|
problemDetails.Detail = unavailableException.Message;
|
|
|
|
|
if (unavailableException.AddRetryAfter)
|
|
|
|
|
context.Response.Headers.RetryAfter = "600";
|
|
|
|
|
break;
|
2024-05-28 07:16:15 +03:00
|
|
|
|
}
|
|
|
|
|
|
2024-12-22 05:25:19 +03:00
|
|
|
|
if (problemDetails.Status == StatusCodes.Status500InternalServerError)
|
2024-10-27 04:36:20 +03:00
|
|
|
|
logger.LogError(exception, "Internal server error when processing the request");
|
2024-10-27 03:02:25 +03:00
|
|
|
|
|
2024-12-22 05:25:19 +03:00
|
|
|
|
context.Response.ContentType = "application/json";
|
|
|
|
|
context.Response.StatusCode = problemDetails.Status.Value;
|
2024-05-28 07:16:15 +03:00
|
|
|
|
|
2024-12-22 05:25:19 +03:00
|
|
|
|
return context.Response.WriteAsync(JsonSerializer.Serialize(problemDetails));
|
2024-05-28 07:16:15 +03:00
|
|
|
|
}
|
|
|
|
|
}
|