-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Expand file tree
/
Copy pathGlobalExceptionHandler.cs
More file actions
92 lines (75 loc) · 3.54 KB
/
GlobalExceptionHandler.cs
File metadata and controls
92 lines (75 loc) · 3.54 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
using FSH.Framework.Core.Context;
using FSH.Framework.Core.Exceptions;
using FSH.Framework.Shared.Multitenancy;
using Finbuckle.MultiTenant.Abstractions;
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Serilog.Context;
namespace FSH.Framework.Web.Exceptions;
public class GlobalExceptionHandler(ILogger<GlobalExceptionHandler> logger)
: IExceptionHandler
{
public async ValueTask<bool> TryHandleAsync(HttpContext httpContext, Exception exception, CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(httpContext);
ArgumentNullException.ThrowIfNull(exception);
var problemDetails = new ProblemDetails
{
Instance = httpContext.Request.Path
};
var statusCode = StatusCodes.Status500InternalServerError;
if (exception is FluentValidation.ValidationException fluentException)
{
statusCode = StatusCodes.Status400BadRequest;
problemDetails.Status = statusCode;
problemDetails.Title = "Validation error";
problemDetails.Detail = "One or more validation errors occurred.";
problemDetails.Type = "https://tools.ietf.org/html/rfc7231#section-6.5.1";
var errors = fluentException.Errors
.GroupBy(e => e.PropertyName)
.ToDictionary(
g => g.Key,
g => g.Select(e => e.ErrorMessage).ToArray());
problemDetails.Extensions["errors"] = errors;
}
else if (exception is CustomException e)
{
statusCode = (int)e.StatusCode;
problemDetails.Status = statusCode;
problemDetails.Title = e.GetType().Name;
problemDetails.Detail = e.Message;
if (e.ErrorMessages is { Count: > 0 })
{
problemDetails.Extensions["errors"] = e.ErrorMessages;
}
}
else
{
statusCode = StatusCodes.Status500InternalServerError;
problemDetails.Status = statusCode;
problemDetails.Title = "An unexpected error occurred";
problemDetails.Detail = "An unexpected error occurred. Please try again later.";
}
httpContext.Response.StatusCode = statusCode;
var multiTenantContextAccessor = httpContext.RequestServices.GetRequiredService<IMultiTenantContextAccessor<AppTenantInfo>>();
var currentUser = httpContext.RequestServices.GetRequiredService<ICurrentUser>();
string? tenantId = multiTenantContextAccessor?.MultiTenantContext?.TenantInfo?.Id;
Guid userId = currentUser.GetUserId();
LogContext.PushProperty("tenant_id", tenantId);
LogContext.PushProperty("user_id", userId);
LogContext.PushProperty("exception_title", problemDetails.Title);
LogContext.PushProperty("exception_detail", problemDetails.Detail);
LogContext.PushProperty("exception_statusCode", problemDetails.Status);
LogContext.PushProperty("exception_stackTrace", exception.StackTrace);
logger.LogError("Exception at {Path} (Tenant: {TenantId}, User: {UserId}) - {Detail}",
httpContext.Request.Path,
tenantId ?? "None",
userId == Guid.Empty ? "Anonymous" : userId.ToString(),
problemDetails.Detail);
await httpContext.Response.WriteAsJsonAsync(problemDetails, cancellationToken).ConfigureAwait(false);
return true;
}
}