Refactor JWT token generation and update password handling logic; add exception handling behavior

This commit is contained in:
masoodafar-web
2025-11-13 21:40:14 +03:30
parent 4b1b135065
commit 8c4b1ab4f4
15 changed files with 199 additions and 34 deletions

View File

@@ -0,0 +1,99 @@
// filepath: /media/masoud/Project/FourSat/CMS/src/CMSMicroservice.WebApi/Common/Behaviours/ExceptionHandlingBehaviour.cs
using System.Text.Json;
using System.Collections.Generic;
using CMSMicroservice.Application.Common.Exceptions;
using Grpc.Core;
using Grpc.Core.Interceptors;
using Microsoft.Extensions.Logging;
namespace CMSMicroservice.WebApi.Common.Behaviours;
public class ExceptionHandlingBehaviour : Interceptor
{
private readonly ILogger<ExceptionHandlingBehaviour> _logger;
public ExceptionHandlingBehaviour(ILogger<ExceptionHandlingBehaviour> logger)
{
_logger = logger;
}
public override async Task<TResponse> UnaryServerHandler<TRequest, TResponse>(
TRequest request,
ServerCallContext context,
UnaryServerMethod<TRequest, TResponse> continuation)
{
try
{
return await continuation(request, context);
}
catch (ValidationException vex)
{
// Flatten validation errors into a trailer so clients can display them
var metadata = new Metadata();
string description = vex.Message;
try
{
if (vex.Errors is { Count: > 0 })
{
var payload = JsonSerializer.Serialize(vex.Errors);
metadata.Add("validation-errors-text", payload);
// Build a human-friendly description out of individual messages
var parts = new List<string>();
foreach (var kv in vex.Errors)
{
foreach (var msg in kv.Value)
{
if (!string.IsNullOrWhiteSpace(kv.Key))
parts.Add($"{kv.Key}: {msg}");
else
parts.Add(msg);
}
}
if (parts.Count > 0)
{
description = string.Join(" | ", parts);
}
}
}
catch
{
// ignore serialization issues, still return InvalidArgument
}
_logger.LogWarning(vex, "Validation failed for request {Method}", context.Method);
throw new RpcException(new Status(StatusCode.InvalidArgument, description), metadata);
}
catch (NotFoundException nfex)
{
_logger.LogInformation(nfex, "Entity not found for request {Method}", context.Method);
throw new RpcException(new Status(StatusCode.NotFound, nfex.Message));
}
catch (UnauthorizedAccessException uaex)
{
_logger.LogInformation(uaex, "Unauthorized access for request {Method}", context.Method);
throw new RpcException(new Status(StatusCode.Unauthenticated, uaex.Message));
}
catch (ForbiddenAccessException fax)
{
_logger.LogInformation(fax, "Forbidden access for request {Method}", context.Method);
throw new RpcException(new Status(StatusCode.PermissionDenied, fax.Message));
}
catch (DuplicateException dex)
{
_logger.LogInformation(dex, "Duplicate resource for request {Method}", context.Method);
throw new RpcException(new Status(StatusCode.AlreadyExists, dex.Message));
}
catch (ArgumentException aex)
{
_logger.LogInformation(aex, "Invalid argument for request {Method}", context.Method);
throw new RpcException(new Status(StatusCode.InvalidArgument, aex.Message));
}
catch (Exception ex)
{
_logger.LogError(ex, "Unhandled exception for request {Method}", context.Method);
// Hide internal details from clients
throw new RpcException(new Status(StatusCode.Internal, "Internal server error"));
}
}
}