initial commit
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace ASPNET.BackEnd.Common.Base;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/[controller]/[action]")]
|
||||
public abstract class BaseApiController : ControllerBase
|
||||
{
|
||||
protected readonly ISender _sender;
|
||||
|
||||
protected BaseApiController(ISender sender)
|
||||
{
|
||||
_sender = sender;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
using ASPNET.BackEnd.Common.Models;
|
||||
using Microsoft.AspNetCore.Diagnostics;
|
||||
|
||||
namespace ASPNET.BackEnd.Common.Handlers;
|
||||
|
||||
public class CustomExceptionHandler : IExceptionHandler
|
||||
{
|
||||
private readonly Dictionary<Type, Func<HttpContext, Exception, Task>> _exceptionHandlers;
|
||||
|
||||
public CustomExceptionHandler()
|
||||
{
|
||||
_exceptionHandlers = new()
|
||||
{
|
||||
{ typeof(Exception), HandleException },
|
||||
};
|
||||
}
|
||||
public async ValueTask<bool> TryHandleAsync(HttpContext httpContext, Exception exception, CancellationToken cancellationToken)
|
||||
{
|
||||
var exceptionType = exception.GetType();
|
||||
|
||||
if (_exceptionHandlers.ContainsKey(exceptionType))
|
||||
{
|
||||
await _exceptionHandlers[exceptionType].Invoke(httpContext, exception);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
await HandleException(httpContext, exception);
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private async Task HandleException(HttpContext httpContext, Exception ex)
|
||||
{
|
||||
var statusCode = httpContext.Response.StatusCode != 200
|
||||
? httpContext.Response.StatusCode
|
||||
: StatusCodes.Status500InternalServerError;
|
||||
|
||||
var errorMessage = ex.Message;
|
||||
|
||||
var result = new ApiErrorResult
|
||||
{
|
||||
Code = statusCode,
|
||||
Message = $"Exception: {errorMessage}",
|
||||
Error = new Error(ex.InnerException?.Message, ex.Source, ex.StackTrace, ex.GetType().Name)
|
||||
};
|
||||
|
||||
httpContext.Response.ContentType = "application/json";
|
||||
await httpContext.Response.WriteAsJsonAsync(result);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
using Microsoft.AspNetCore.Diagnostics;
|
||||
|
||||
namespace ASPNET.BackEnd.Common.Middlewares;
|
||||
|
||||
public class GlobalApiExceptionHandlerMiddleware
|
||||
{
|
||||
private readonly RequestDelegate _next;
|
||||
|
||||
public GlobalApiExceptionHandlerMiddleware(RequestDelegate next)
|
||||
{
|
||||
_next = next;
|
||||
}
|
||||
|
||||
public async Task InvokeAsync(HttpContext httpContext, IExceptionHandler customExceptionHandler)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _next(httpContext);
|
||||
|
||||
switch (httpContext.Response.StatusCode)
|
||||
{
|
||||
case StatusCodes.Status401Unauthorized:
|
||||
await customExceptionHandler.TryHandleAsync(
|
||||
httpContext,
|
||||
new UnauthorizedAccessException("Unauthorized - Token missing or invalid"),
|
||||
CancellationToken.None
|
||||
);
|
||||
break;
|
||||
|
||||
case StatusCodes.Status403Forbidden:
|
||||
await customExceptionHandler.TryHandleAsync(
|
||||
httpContext,
|
||||
new Exception("Forbidden - Access denied"),
|
||||
CancellationToken.None
|
||||
);
|
||||
break;
|
||||
|
||||
default:
|
||||
// Let other status codes pass through
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await customExceptionHandler.TryHandleAsync(httpContext, ex, CancellationToken.None);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace ASPNET.BackEnd.Common.Models;
|
||||
|
||||
public class ApiErrorResult
|
||||
{
|
||||
public int? Code { get; init; }
|
||||
public string? Message { get; init; }
|
||||
public Error? Error { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace ASPNET.BackEnd.Common.Models;
|
||||
|
||||
public class ApiSuccessResult<T>
|
||||
{
|
||||
public int? Code { get; init; }
|
||||
public string? Message { get; init; }
|
||||
public T? Content { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
namespace ASPNET.BackEnd.Common.Models;
|
||||
|
||||
public class Error
|
||||
{
|
||||
public Error(
|
||||
string? innerException,
|
||||
string? source,
|
||||
string? stackTrace,
|
||||
string? exceptionType)
|
||||
{
|
||||
Ref = "https://datatracker.ietf.org/doc/html/rfc9110";
|
||||
InnerException = innerException;
|
||||
Source = source?.Trim();
|
||||
StackTrace = stackTrace?.Trim();
|
||||
ExceptionType = exceptionType;
|
||||
}
|
||||
public string Ref { get; set; }
|
||||
public string? ExceptionType { get; init; }
|
||||
public string? InnerException { get; init; }
|
||||
public string? Source { get; init; }
|
||||
public string? StackTrace { get; init; }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user