Answer the question
In order to leave comments, you need to log in
How to properly organize Exception Handling in a business application?
Good afternoon!
There is a business application (.NET) that consists of several layers, for example:
public class CreateOrderCommand : IRequest
{
...
public PaymentType PaymentType { get; set; }
public string CountryCode { get; set; }
...
}
internal class CreateOrderCommandHandler : IRequestHandler<CreateOrderCommand, int>
{
private readonly IDbContext _dbContext;
public CreateOrderCommandHandler(IDbContext dbContext)
{
_dbContext = dbContext;
}
public async Task<int> Handle(CreateOrderCommand command, CancellationToken cancellationToken)
{
...
if (command.PaymentType == PaymentType.PayPal && command.CountryCode == "RU")
{
throw new AppException(Messages.InvalidPaymentType);
}
...
}
}
public class AppException : Exception
{
public class AppException(string message) : base(message)
{}
}
public class HandleErrorAttribute : TypeFilterAttribute
{
....
public void OnException(ExceptionContext context)
{
if (context.ExceptionHandled)
return;
...
if (context.HttpContext.Request.IsAjaxRequest())
HandleAjaxRequestException(context);
else
HandleRequestException(context);
context.ExceptionHandled = true;
}
private void HandleAjaxRequestException(ExceptionContext context)
{
string message = Messages.Error500;
if (context.Exception is AppException)
{
message = context.Exception.Message; // Используем текст исключения заданный на Domain/Application слоях
}
context.Result = new JsonResult(new { Ok = false, Message = message});
}
}
public class InvalidPaymentTypeException : Exception
{
public InvalidPaymentTypeException(PaymentType paymentType, string countryCode)
: base($"Invalid payment type \"{paymentType}\" for country \"{countryCode}\".")
{}
}
internal class CreateOrderCommandHandler : IRequestHandler<CreateOrderCommand, int>
{
...
public async Task<int> Handle(CreateOrderCommand command, CancellationToken cancellationToken)
{
...
if (command.PaymentType == PaymentType.PayPal && command.CountryCode == "RU")
{
throw new InvalidPaymentTypeException(command.PaymentType, command.CountryCode);
}
...
}
}
public class HandleErrorAttribute : TypeFilterAttribute
{
....
private void HandleAjaxRequestException(ExceptionContext context)
{
string message = Messages.Error500;
if (context.Exception is InvalidPaymentTypeException)
{
message = Messages.InvalidPaymentType; // Локализованный текст из файла ресурсов
}
else if (context.Exception is InvalidCredentialsException)
{
message = Messages.InvalidCredentials; // Локализованный текст из файла ресурсов
}
// И так все исключения
else {
message = Messages.Error500; // Внутренняя ошибка
}
context.Result = new JsonResult(new { Ok = false, Message = message});
}
}
public abstract class LocalizedException : Exception
{
public string LocalizedMessage { get; }
protected LocalizedException(string message, string localizedMessage)
: base(message)
{
LocalizedMessage = localizedMessage;
}
}
public class InvalidPaymentTypeException : LocalizedException
{
public InvalidPaymentTypeException(PaymentType paymentType, string countryCode)
: base(
message: $"Invalid payment type \"{paymentType}\" for country \"{countryCode}\".",
localizedMessage: Messages.InvaidPaymentType // Вторым параметром передаем локализованную строку из файла ресурсов
)
{}
}
...
private void HandleAjaxRequestException(ExceptionContext context)
{
string message = Messages.Error500;
if (context.Exception is LocalizedException)
{
message = context.Exception.LocalizedMessage;
}
context.Result = new JsonResult(new { Ok = false, Message = message});
}
...
Answer the question
In order to leave comments, you need to log in
Didn't find what you were looking for?
Ask your questionAsk a Question
731 491 924 answers to any question