astral-api/Astral.ApiServer/Middleware/ExceptionMiddleware.cs
Mike 81aa0ec1c0
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/pr/woodpecker Pipeline was successful
WIP heartbeat and user presence
2024-12-15 16:06:14 +00:00

110 lines
3.7 KiB
C#

// <copyright file="ExceptionMiddleware.cs" company="alveus.dev">
// Copyright (c) alveus.dev. All rights reserved. Licensed under the MIT License.
// </copyright>
using System.Net;
using Astral.ApiServer.Core;
using Astral.ApiServer.Models.Common;
using Astral.Core.Constants;
using Astral.Core.Exceptions;
using FluentValidation;
namespace Astral.ApiServer.Middleware;
/// <summary>
/// Handle exceptions caught in a request.
/// </summary>
public class ExceptionMiddleware
{
private readonly ILogger<ExceptionMiddleware> _logger;
private readonly RequestDelegate _next;
/// <summary>
/// Initializes a new instance of the <see cref="ExceptionMiddleware" /> class.
/// </summary>
/// <param name="next">Instance of <see cref="RequestDelegate" />.</param>
/// <param name="logger">Instance of <see cref="ILogger" />.</param>
public ExceptionMiddleware(
RequestDelegate next,
ILogger<ExceptionMiddleware> logger)
{
_next = next;
_logger = logger;
}
/// <summary>
/// Invoke middleware.
/// </summary>
/// <param name="httpContext">Instance of <see cref="HttpContext" />.</param>
public async Task InvokeAsync(HttpContext httpContext)
{
try
{
await _next(httpContext);
}
catch (ServiceException exception)
{
await HandleServiceExceptionAsync(httpContext, exception);
}
catch (ValidationException exception)
{
await HandleValidationExceptionAsync(httpContext, exception);
}
catch (Exception exception)
{
_logger.LogError(
"Unhandled Exception: {path} using {method}: {exception}",
httpContext.Request.Path,
httpContext.Request.Method,
exception);
await HandleOtherExceptionAsync(httpContext);
}
}
/// <summary>
/// Api exception.
/// </summary>
/// <param name="context">Instance of <see cref="HttpContext" />.</param>
/// <param name="exception">Instance of <see cref="ValidationException" />.</param>
private static async Task HandleServiceExceptionAsync(HttpContext context, ServiceException exception)
{
context.Response.ContentType = "application/json";
context.Response.StatusCode = (int)exception.HttpStatusCode;
await context.Response.WriteAsJsonAsync(new ErrorResponseModel
{
Error = exception.ErrorCode,
Message = exception.ErrorMessage
});
}
/// <summary>
/// Validation failed.
/// </summary>
/// <param name="context">Instance of <see cref="HttpContext" />.</param>
/// <param name="exception">Instance of <see cref="ValidationException" />.</param>
private static async Task HandleValidationExceptionAsync(HttpContext context, ValidationException exception)
{
context.Response.ContentType = "application/json";
context.Response.StatusCode = StatusCodes.Status422UnprocessableEntity;
await context.Response.WriteAsJsonAsync(new ErrorResponseModel
{
Error = CoreErrorCodes.ValidationError,
Message = exception.Message
});
}
/// <summary>
/// Unexpected exception.
/// </summary>
/// <param name="context">Instance of <see cref="HttpContext" />.</param>
private static async Task HandleOtherExceptionAsync(HttpContext context)
{
context.Response.ContentType = "application/json";
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
await context.Response.WriteAsJsonAsync(new ErrorResponseModel
{
Error = ApiErrorCodes.UnknownError,
Message = "Something went wrong. Try again later"
});
}
}