initial commit

This commit is contained in:
2026-07-21 15:03:40 +07:00
commit 5c20bb8a9e
2752 changed files with 864209 additions and 0 deletions
@@ -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; }
}