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); } }