using System.Net;
using System.Text.Json;
using SameBeat.Domain.Wyjatki;
namespace SameBeat.Api.Middleware;
///
/// Globalny lapacz nieobsluzonych wyjatkow. Kazdy nieprzewidziany blad ma wrocic do klienta jako
/// czytelny JSON zamiast surowego stack trace'u albo pustej odpowiedzi 500 -- zeby frontend zawsze
/// mial cos sensownego do pokazania uzytkownikowi.
///
public class ObslugaWyjatkowMiddleware
{
private readonly RequestDelegate _nastepny;
private readonly ILogger _logger;
public ObslugaWyjatkowMiddleware(RequestDelegate nastepny, ILogger logger)
{
_nastepny = nastepny;
_logger = logger;
}
public async Task InvokeAsync(HttpContext kontekst)
{
try
{
await _nastepny(kontekst);
}
catch (WyjatekDomenowy wyjatek)
{
// Naruszenie niezmiennika domenowego (blad programistyczny) -- 400, z komunikatem
// bezpiecznym do pokazania (encje same pilnuja, zeby nie przeciekly przez niego dane wrazliwe).
_logger.LogWarning(wyjatek, "Naruszenie regul domenowych.");
await ZapiszOdpowiedzAsync(kontekst, HttpStatusCode.BadRequest, wyjatek.Message);
}
catch (Exception wyjatek)
{
_logger.LogError(wyjatek, "Nieobsluzony wyjatek podczas przetwarzania zadania {Sciezka}.", kontekst.Request.Path);
await ZapiszOdpowiedzAsync(kontekst, HttpStatusCode.InternalServerError, "Wystapil nieoczekiwany blad serwera.");
}
}
private static async Task ZapiszOdpowiedzAsync(HttpContext kontekst, HttpStatusCode kodStatusu, string komunikat)
{
if (kontekst.Response.HasStarted)
{
// Odpowiedz juz zaczela sie wysylac (np. strumieniowanie) -- nie da sie jej podmienic.
return;
}
kontekst.Response.Clear();
kontekst.Response.StatusCode = (int)kodStatusu;
kontekst.Response.ContentType = "application/json";
var tresc = JsonSerializer.Serialize(new { blad = komunikat });
await kontekst.Response.WriteAsync(tresc);
}
}