Add Global Exception Handling
This commit is contained in:
@@ -0,0 +1,7 @@
|
|||||||
|
namespace Imprink.Application.Exceptions;
|
||||||
|
|
||||||
|
public abstract class BaseApplicationException : Exception
|
||||||
|
{
|
||||||
|
protected BaseApplicationException(string message) : base(message) { }
|
||||||
|
protected BaseApplicationException(string message, Exception innerException) : base(message, innerException) { }
|
||||||
|
}
|
||||||
7
src/Imprink.Application/Exceptions/NotFoundException.cs
Normal file
7
src/Imprink.Application/Exceptions/NotFoundException.cs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
namespace Imprink.Application.Exceptions;
|
||||||
|
|
||||||
|
public class NotFoundException : BaseApplicationException
|
||||||
|
{
|
||||||
|
public NotFoundException(string message) : base(message) { }
|
||||||
|
public NotFoundException(string message, Exception innerException) : base(message, innerException) { }
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using Imprink.Application.Exceptions;
|
||||||
using Imprink.Application.Users.Dtos;
|
using Imprink.Application.Users.Dtos;
|
||||||
using Imprink.Domain.Entities.Users;
|
using Imprink.Domain.Entities.Users;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
@@ -10,7 +11,8 @@ public class DeleteUserRoleHandler(IUnitOfWork uw) : IRequestHandler<DeleteUserR
|
|||||||
{
|
{
|
||||||
public async Task<UserRoleDto?> Handle(DeleteUserRoleCommand request, CancellationToken cancellationToken)
|
public async Task<UserRoleDto?> Handle(DeleteUserRoleCommand request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (!await uw.UserRepository.UserExistsAsync(request.Sub, cancellationToken)) return null;
|
if (!await uw.UserRepository.UserExistsAsync(request.Sub, cancellationToken))
|
||||||
|
throw new NotFoundException("User with ID: " + request.Sub + " does not exist.");
|
||||||
|
|
||||||
var userRole = new UserRole
|
var userRole = new UserRole
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using Imprink.Application.Exceptions;
|
||||||
using Imprink.Application.Users.Dtos;
|
using Imprink.Application.Users.Dtos;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
|
|
||||||
@@ -9,7 +10,8 @@ public class GetUserRolesHandler(IUnitOfWork uw): IRequestHandler<GetUserRolesCo
|
|||||||
{
|
{
|
||||||
public async Task<IEnumerable<RoleDto>> Handle(GetUserRolesCommand request, CancellationToken cancellationToken)
|
public async Task<IEnumerable<RoleDto>> Handle(GetUserRolesCommand request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (!await uw.UserRepository.UserExistsAsync(request.Sub, cancellationToken)) return [];
|
if (!await uw.UserRepository.UserExistsAsync(request.Sub, cancellationToken))
|
||||||
|
throw new NotFoundException("User with ID: " + request.Sub + " does not exist.");
|
||||||
|
|
||||||
var roles = await uw.UserRoleRepository.GetUserRolesAsync(request.Sub, cancellationToken);
|
var roles = await uw.UserRoleRepository.GetUserRolesAsync(request.Sub, cancellationToken);
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using Imprink.Application.Exceptions;
|
||||||
using Imprink.Application.Users.Dtos;
|
using Imprink.Application.Users.Dtos;
|
||||||
using Imprink.Domain.Entities.Users;
|
using Imprink.Domain.Entities.Users;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
@@ -10,7 +11,8 @@ public class SetUserRoleHandler(IUnitOfWork uw) : IRequestHandler<SetUserRoleCom
|
|||||||
{
|
{
|
||||||
public async Task<UserRoleDto?> Handle(SetUserRoleCommand request, CancellationToken cancellationToken)
|
public async Task<UserRoleDto?> Handle(SetUserRoleCommand request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (!await uw.UserRepository.UserExistsAsync(request.Sub, cancellationToken)) return null;
|
if (!await uw.UserRepository.UserExistsAsync(request.Sub, cancellationToken))
|
||||||
|
throw new NotFoundException("User with ID: " + request.Sub + " does not exist.");
|
||||||
|
|
||||||
var userRole = new UserRole
|
var userRole = new UserRole
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ namespace Imprink.WebApi.Controllers.Users;
|
|||||||
public class UserRoleController(IMediator mediator) : ControllerBase
|
public class UserRoleController(IMediator mediator) : ControllerBase
|
||||||
{
|
{
|
||||||
[Authorize]
|
[Authorize]
|
||||||
[HttpGet("/me")]
|
[HttpGet("me")]
|
||||||
public async Task<IActionResult> GetMyRoles()
|
public async Task<IActionResult> GetMyRoles()
|
||||||
{
|
{
|
||||||
var claims = User.Claims as Claim[] ?? User.Claims.ToArray();
|
var claims = User.Claims as Claim[] ?? User.Claims.ToArray();
|
||||||
@@ -23,7 +23,7 @@ public class UserRoleController(IMediator mediator) : ControllerBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Authorize(Roles = "Admin")]
|
[Authorize(Roles = "Admin")]
|
||||||
[HttpPost("/set")]
|
[HttpPost("set")]
|
||||||
public async Task<IActionResult> SetUserRole(SetUserRoleCommand command)
|
public async Task<IActionResult> SetUserRole(SetUserRoleCommand command)
|
||||||
{
|
{
|
||||||
var userRole = await mediator.Send(command);
|
var userRole = await mediator.Send(command);
|
||||||
@@ -35,7 +35,7 @@ public class UserRoleController(IMediator mediator) : ControllerBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Authorize(Roles = "Admin")]
|
[Authorize(Roles = "Admin")]
|
||||||
[HttpPost("/unset")]
|
[HttpPost("unset")]
|
||||||
public async Task<IActionResult> UnsetUserRole(DeleteUserRoleCommand command)
|
public async Task<IActionResult> UnsetUserRole(DeleteUserRoleCommand command)
|
||||||
{
|
{
|
||||||
var userRole = await mediator.Send(command);
|
var userRole = await mediator.Send(command);
|
||||||
|
|||||||
75
src/Imprink.WebApi/Middleware/ExceptionHandlingMiddleware.cs
Normal file
75
src/Imprink.WebApi/Middleware/ExceptionHandlingMiddleware.cs
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
using System.Net;
|
||||||
|
using System.Text.Json;
|
||||||
|
using Imprink.Application.Exceptions;
|
||||||
|
|
||||||
|
namespace Imprink.WebApi.Middleware;
|
||||||
|
|
||||||
|
public class ExceptionHandlingMiddleware(
|
||||||
|
RequestDelegate next,
|
||||||
|
ILogger<ExceptionHandlingMiddleware> logger)
|
||||||
|
{
|
||||||
|
public async Task InvokeAsync(HttpContext context)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await next(context);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
var (_, _, shouldLog) = GetStatusCodeAndMessage(ex);
|
||||||
|
|
||||||
|
if (shouldLog)
|
||||||
|
{
|
||||||
|
logger.LogError(ex, "An unhandled exception occurred: {Message}", ex.Message);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.LogInformation("Handled: {Message}", ex.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
await HandleExceptionAsync(context, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task HandleExceptionAsync(HttpContext context, Exception exception)
|
||||||
|
{
|
||||||
|
context.Response.ContentType = "application/json";
|
||||||
|
|
||||||
|
var (statusCode, message, _) = GetStatusCodeAndMessage(exception);
|
||||||
|
|
||||||
|
context.Response.StatusCode = (int)statusCode;
|
||||||
|
|
||||||
|
var response = new
|
||||||
|
{
|
||||||
|
error = new
|
||||||
|
{
|
||||||
|
message,
|
||||||
|
timestamp = DateTime.UtcNow,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var jsonResponse = JsonSerializer.Serialize(response, new JsonSerializerOptions
|
||||||
|
{
|
||||||
|
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
|
||||||
|
});
|
||||||
|
|
||||||
|
await context.Response.WriteAsync(jsonResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static (HttpStatusCode statusCode, string message, bool shouldLog) GetStatusCodeAndMessage(Exception exception)
|
||||||
|
{
|
||||||
|
return exception switch
|
||||||
|
{
|
||||||
|
NotFoundException => (HttpStatusCode.NotFound, exception.Message, false),
|
||||||
|
_ => (HttpStatusCode.InternalServerError, "An internal server error occurred", true)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class GlobalExceptionHandlingMiddlewareExtensions
|
||||||
|
{
|
||||||
|
public static void UseGlobalExceptionHandling(this IApplicationBuilder builder)
|
||||||
|
{
|
||||||
|
builder.UseMiddleware<ExceptionHandlingMiddleware>();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -140,13 +140,13 @@ public static class Startup
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
app.UseGlobalExceptionHandling();
|
||||||
app.UseRequestTiming();
|
app.UseRequestTiming();
|
||||||
|
|
||||||
if (env.IsDevelopment())
|
if (env.IsDevelopment())
|
||||||
{
|
{
|
||||||
app.UseSwagger();
|
app.UseSwagger();
|
||||||
app.UseSwaggerUI();
|
app.UseSwaggerUI();
|
||||||
app.UseDeveloperExceptionPage();
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user