using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; namespace Kit.Helpers; public enum StackTraceModesJson { None = 0, Text = 1, Html = 2 } public enum StackTraceModesHtml { None = 0, Text = 1, Html = 2, CommentText = 3, CommentHtml = 4, } public interface IConfigurationExceptionHandler { StackTraceModesJson StackTraceModeJson { get; } StackTraceModesHtml StackTraceModeHtml { get; } } public class ConfigurationExceptionHandler : IConfigurationExceptionHandler { public StackTraceModesJson StackTraceModeJson { get; private set; } public StackTraceModesHtml StackTraceModeHtml { get; private set; } public ConfigurationExceptionHandler(Microsoft.Extensions.Configuration.IConfiguration configuration) { var helper = new ConfigurationHelper(configuration, "ExceptionHandler"); StackTraceModeJson = helper.GetAppSettingValue("StackTraceModeJson", required: false, defaultValue: StackTraceModesJson.None.ToString()).ParseToEnum(); StackTraceModeHtml = helper.GetAppSettingValue("StackTraceModeHtml", required: false, defaultValue: StackTraceModesHtml.None.ToString()).ParseToEnum(); } } public static class ExceptionHandlerMiddlewareExt { public static IServiceCollection InitExceptionHandlerMiddleware(this IServiceCollection builder, IConfigurationExceptionHandler configuration) { ExceptionHandlerMiddleware.Init(configuration); return builder; } } public class ExceptionHandlerMiddleware { private static IConfigurationExceptionHandler? _exceptionHandler; private static StackTraceModesJson _stackTraceModeJson => _exceptionHandler?.StackTraceModeJson ?? StackTraceModesJson.None; private static StackTraceModesHtml _stackTraceModeHtml => _exceptionHandler?.StackTraceModeHtml ?? StackTraceModesHtml.None; private readonly RequestDelegate _next; public static void Init(IConfigurationExceptionHandler exceptionHandler) { _exceptionHandler = exceptionHandler; } public ExceptionHandlerMiddleware(RequestDelegate next) { _next = next; } public async Task Invoke(HttpContext context) { try { await _next(context); } catch (Exception ex) { int statusCode = 0; string errorMessage = string.Empty; string errorStackTrace = string.Empty; string? redirectUrl = null; bool forceJson = false; switch (ex) { case AuthenticationException exception: statusCode = StatusCodes.Status401Unauthorized; errorMessage = exception.Message; redirectUrl = exception.RedirectUrl; forceJson = true; break; case AuthorizationException exception: statusCode = StatusCodes.Status403Forbidden; errorMessage = exception.Message; redirectUrl = exception.RedirectUrl; forceJson = true; break; case InvalidOperationException exception: statusCode = StatusCodes.Status409Conflict; errorMessage = exception.Message; break; default: statusCode = StatusCodes.Status500InternalServerError; errorMessage = "На сервере произошла неизвестная ошибка" + Environment.NewLine + ex.Message; break; } context.Response.StatusCode = statusCode; if (forceJson || context.NeedResponseJsonResult(ex)) { switch (_stackTraceModeJson) { case StackTraceModesJson.Text: errorStackTrace = ex?.GetInfoAsPlainText() ?? string.Empty; break; case StackTraceModesJson.Html: errorStackTrace = ex?.GetInfoAsHtml() ?? string.Empty; break; } string json = new JsonResultError { ErrorMessage = errorMessage, ErrorStackTrace = errorStackTrace, RedirectUrl = redirectUrl }.JsonSerialize(enableCyrillic: true, propertyNamingPolicy: System.Text.Json.JsonNamingPolicy.CamelCase); context.Response.ContentType = "application/json; charset=utf-8"; await context.Response.WriteAsync(json, System.Text.Encoding.UTF8); } else { switch (_stackTraceModeHtml) { case StackTraceModesHtml.Text: errorStackTrace = ex.GetInfoAsPlainText() ?? string.Empty; break; case StackTraceModesHtml.Html: errorStackTrace = ex.GetInfoAsHtml() ?? string.Empty; break; case StackTraceModesHtml.CommentText: errorStackTrace = ""; break; case StackTraceModesHtml.CommentHtml: errorStackTrace = ""; break; } errorMessage += Environment.NewLine + errorStackTrace; context.Response.ContentType = "text/html; charset=utf-8"; await context.Response.WriteAsync(errorMessage, System.Text.Encoding.UTF8); } } } }