From c41494ee5351a742bf5b8acb545d45d3f6bd4161 Mon Sep 17 00:00:00 2001 From: Ion Dormenco Date: Sat, 14 Oct 2023 18:33:47 +0300 Subject: [PATCH] Add global exception handler --- .../Extensions/ExceptionHandler.cs | 59 +++++++++++++++++++ src/api/VoteMonitor.Api/Program.cs | 4 +- 2 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 src/api/VoteMonitor.Api/Extensions/ExceptionHandler.cs diff --git a/src/api/VoteMonitor.Api/Extensions/ExceptionHandler.cs b/src/api/VoteMonitor.Api/Extensions/ExceptionHandler.cs new file mode 100644 index 00000000..028aeda9 --- /dev/null +++ b/src/api/VoteMonitor.Api/Extensions/ExceptionHandler.cs @@ -0,0 +1,59 @@ +using Microsoft.AspNetCore.Diagnostics; +using Microsoft.AspNetCore.Mvc; +using Serilog; +using System; +using System.Net; + +namespace VoteMonitor.Api.Extensions; + +/// +/// extensions for global exception handling +/// +public static class ExceptionHandlerExtensions +{ + /// + /// registers the default global exception handler which will log the exceptions on the server and return a user-friendly json response to the client when unhandled exceptions occur. + /// TIP: when using this exception handler, you may want to turn off the asp.net core exception middleware logging to avoid duplication like so: + /// + /// "Logging": { "LogLevel": { "Default": "Warning", "Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware": "None" } } + /// + /// + public static IApplicationBuilder UseDefaultExceptionHandler(this IApplicationBuilder app) + { + app.UseExceptionHandler(errApp => + { + errApp.Run(async ctx => + { + var exHandlerFeature = ctx.Features.Get(); + if (exHandlerFeature is not null) + { + + var http = exHandlerFeature.Endpoint?.DisplayName?.Split(" => ")[0]; + var type = exHandlerFeature.Error.GetType().Name; + var error = exHandlerFeature.Error.Message; + var msg = +$@"================================= +{http} +TYPE: {type} +REASON: {error} +--------------------------------- +{exHandlerFeature.Error.StackTrace}"; + + Log.Error("{@http}{@type}{@reason}{@exception}", http, type, error, exHandlerFeature.Error); + + ctx.Response.StatusCode = (int)HttpStatusCode.InternalServerError; + ctx.Response.ContentType = "application/problem+json"; + await ctx.Response.WriteAsJsonAsync(new ProblemDetails + { + Title = "An error occurred", + Detail = error, + Type = type, + Status = (int)HttpStatusCode.BadRequest + }); + } + }); + }); + + return app; + } +} diff --git a/src/api/VoteMonitor.Api/Program.cs b/src/api/VoteMonitor.Api/Program.cs index a668da8e..dca768aa 100644 --- a/src/api/VoteMonitor.Api/Program.cs +++ b/src/api/VoteMonitor.Api/Program.cs @@ -42,9 +42,10 @@ .AllowAnyHeader(); })); - var app = builder.Build(); +app.UseDefaultExceptionHandler(); app.UseSerilogRequestLogging(); + app.UseStaticFiles(); if (builder.Environment.IsDevelopment()) { @@ -58,7 +59,6 @@ app.UseSwaggerAndUi(); app.UseCors(CorsPolicyName); - app.MapControllers(); app.MapHealthChecks("/health", new HealthCheckOptions {