Format ./web/backend/API/API.Core

This commit is contained in:
Aaron Po
2026-06-20 15:14:20 -04:00
parent 3d046e369c
commit 678ee21bbd
6 changed files with 57 additions and 92 deletions

View File

@@ -8,15 +8,9 @@
</PropertyGroup>
<ItemGroup>
<PackageReference
Include="Microsoft.AspNetCore.OpenApi"
Version="9.0.11"
/>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.11" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2" />
<PackageReference
Include="FluentValidation.AspNetCore"
Version="11.3.0"
/>
<PackageReference Include="FluentValidation.AspNetCore" Version="11.3.0" />
<PackageReference Include="MediatR" Version="12.4.1" />
</ItemGroup>

View File

@@ -44,29 +44,19 @@ public class JwtAuthenticationHandler(
{
// Use the same access-token secret source as TokenService to avoid mismatched validation.
string? secret = Environment.GetEnvironmentVariable("ACCESS_TOKEN_SECRET");
if (string.IsNullOrWhiteSpace(secret)) secret = configuration["Jwt:SecretKey"];
if (string.IsNullOrWhiteSpace(secret))
secret = configuration["Jwt:SecretKey"];
if (string.IsNullOrWhiteSpace(secret)) return AuthenticateResult.Fail("JWT secret is not configured");
if (string.IsNullOrWhiteSpace(secret))
return AuthenticateResult.Fail("JWT secret is not configured");
// Check if Authorization header exists
if (
!Request.Headers.TryGetValue(
"Authorization",
out StringValues authHeaderValue
)
)
if (!Request.Headers.TryGetValue("Authorization", out StringValues authHeaderValue))
return AuthenticateResult.Fail("Authorization header is missing");
string authHeader = authHeaderValue.ToString();
if (
!authHeader.StartsWith(
"Bearer ",
StringComparison.OrdinalIgnoreCase
)
)
return AuthenticateResult.Fail(
"Invalid authorization header format"
);
if (!authHeader.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
return AuthenticateResult.Fail("Invalid authorization header format");
string token = authHeader.Substring("Bearer ".Length).Trim();
@@ -81,9 +71,7 @@ public class JwtAuthenticationHandler(
}
catch (Exception ex)
{
return AuthenticateResult.Fail(
$"Token validation failed: {ex.Message}"
);
return AuthenticateResult.Fail($"Token validation failed: {ex.Message}");
}
}
@@ -97,7 +85,10 @@ public class JwtAuthenticationHandler(
Response.ContentType = "application/json";
Response.StatusCode = 401;
ResponseBody response = new() { Message = "Unauthorized: Invalid or missing authentication token" };
ResponseBody response = new()
{
Message = "Unauthorized: Invalid or missing authentication token",
};
await Response.WriteAsJsonAsync(response);
}
}
@@ -106,6 +97,4 @@ public class JwtAuthenticationHandler(
/// Options for the <c>JWT</c> authentication scheme handled by <see cref="JwtAuthenticationHandler" />.
/// </summary>
/// <remarks>No additional options are defined beyond those provided by <see cref="AuthenticationSchemeOptions" />.</remarks>
public class JwtAuthenticationOptions : AuthenticationSchemeOptions
{
}
public class JwtAuthenticationOptions : AuthenticationSchemeOptions { }

View File

@@ -31,7 +31,7 @@ public class ProtectedController : ControllerBase
new ResponseBody<object>
{
Message = "Protected endpoint accessed successfully",
Payload = new { userId, username }
Payload = new { userId, username },
}
);
}

View File

@@ -14,8 +14,7 @@ namespace API.Core;
/// consistent JSON error responses with appropriate HTTP status codes.
/// </summary>
/// <param name="logger">Logger used to record unhandled exceptions before they are translated into a response.</param>
public class GlobalExceptionFilter(ILogger<GlobalExceptionFilter> logger)
: IExceptionFilter
public class GlobalExceptionFilter(ILogger<GlobalExceptionFilter> logger) : IExceptionFilter
{
/// <summary>
/// Logs the exception and sets <see cref="ExceptionContext.Result" /> to an appropriate error response,
@@ -84,10 +83,7 @@ public class GlobalExceptionFilter(ILogger<GlobalExceptionFilter> logger)
case ValidationException fluentValidationException:
Dictionary<string, string[]> errors = fluentValidationException
.Errors.GroupBy(e => e.PropertyName)
.ToDictionary(
g => g.Key,
g => g.Select(e => e.ErrorMessage).ToArray()
);
.ToDictionary(g => g.Key, g => g.Select(e => e.ErrorMessage).ToArray());
context.Result = new BadRequestObjectResult(
new { message = "Validation failed", errors }
@@ -96,41 +92,33 @@ public class GlobalExceptionFilter(ILogger<GlobalExceptionFilter> logger)
break;
case ConflictException ex:
context.Result = new ObjectResult(
new ResponseBody { Message = ex.Message }
)
context.Result = new ObjectResult(new ResponseBody { Message = ex.Message })
{
StatusCode = 409
StatusCode = 409,
};
context.ExceptionHandled = true;
break;
case NotFoundException ex:
context.Result = new ObjectResult(
new ResponseBody { Message = ex.Message }
)
context.Result = new ObjectResult(new ResponseBody { Message = ex.Message })
{
StatusCode = 404
StatusCode = 404,
};
context.ExceptionHandled = true;
break;
case UnauthorizedException ex:
context.Result = new ObjectResult(
new ResponseBody { Message = ex.Message }
)
context.Result = new ObjectResult(new ResponseBody { Message = ex.Message })
{
StatusCode = 401
StatusCode = 401,
};
context.ExceptionHandled = true;
break;
case ForbiddenException ex:
context.Result = new ObjectResult(
new ResponseBody { Message = ex.Message }
)
context.Result = new ObjectResult(new ResponseBody { Message = ex.Message })
{
StatusCode = 403
StatusCode = 403,
};
context.ExceptionHandled = true;
break;
@@ -140,30 +128,25 @@ public class GlobalExceptionFilter(ILogger<GlobalExceptionFilter> logger)
new ResponseBody { Message = "A database error occurred." }
)
{
StatusCode = 503
StatusCode = 503,
};
context.ExceptionHandled = true;
break;
case Domain.Exceptions.ValidationException ex:
context.Result = new ObjectResult(
new ResponseBody { Message = ex.Message }
)
context.Result = new ObjectResult(new ResponseBody { Message = ex.Message })
{
StatusCode = 400
StatusCode = 400,
};
context.ExceptionHandled = true;
break;
default:
context.Result = new ObjectResult(
new ResponseBody
{
Message = "An unexpected error occurred"
}
new ResponseBody { Message = "An unexpected error occurred" }
)
{
StatusCode = 500
StatusCode = 500,
};
context.ExceptionHandled = true;
break;

View File

@@ -17,7 +17,11 @@ using Shared.Application.Behaviors;
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
// Global Exception Filter
builder.Services.AddControllers(options => { options.Filters.Add<GlobalExceptionFilter>(); })
builder
.Services.AddControllers(options =>
{
options.Filters.Add<GlobalExceptionFilter>();
})
.AddApplicationPart(typeof(BreweryController).Assembly)
.AddApplicationPart(typeof(UserController).Assembly)
.AddApplicationPart(typeof(AuthController).Assembly);
@@ -51,14 +55,12 @@ builder.Services.AddHealthChecks();
// Configure logging for container output
builder.Logging.ClearProviders();
builder.Logging.AddConsole();
if (!builder.Environment.IsProduction()) builder.Logging.AddDebug();
if (!builder.Environment.IsProduction())
builder.Logging.AddDebug();
// Configure Dependency Injection -------------------------------------------------------------------------------------
builder.Services.AddSingleton<
ISqlConnectionFactory,
DefaultSqlConnectionFactory
>();
builder.Services.AddSingleton<ISqlConnectionFactory, DefaultSqlConnectionFactory>();
builder.Services.AddFeaturesBreweries();
builder.Services.AddFeaturesUserManagement();
@@ -75,10 +77,7 @@ builder.Services.AddScoped<GlobalExceptionFilter>();
// Configure JWT Authentication
builder
.Services.AddAuthentication("JWT")
.AddScheme<JwtAuthenticationOptions, JwtAuthenticationHandler>(
"JWT",
options => { }
);
.AddScheme<JwtAuthenticationOptions, JwtAuthenticationHandler>("JWT", options => { });
builder.Services.AddAuthorization();