مدیریت خطاها در MediatR با استفاده از Pipeline Behaviors و IRequestExceptionHandler
نویسنده: محمد شریفی
تاریخ: ۱۴۰۳/۰۹/۲۰ ۰۹:۵۵
آدرس: www.dntips.ir
| مطالب | ۳۶۹۴ |
| نویسندگان | ۲۷۶ |
| گروههای مطالب | ۱۰۲۴ |
| نقشههای راه | ۱۱۹ |
| دورهها | ۱۴ |
| اشتراکها | ۱۷۹۱۴ |
public class MyRequestHandler : IRequestHandler<MyRequest, string>
{
public Task<string> Handle(MyRequest request, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(request.Input))
return Task.FromResult("Input cannot be empty");
throw new InvalidOperationException("An unexpected error occurred during processing.");
return Task.FromResult($"Processed: {request.Input}");
}
}public class ExceptionHandlingBehavior<TRequest, TResponse>
: IPipelineBehavior<TRequest, TResponse>
{
private readonly ILogger<ExceptionHandlingBehavior<TRequest, TResponse>> _logger;
public ExceptionHandlingBehavior(ILogger<ExceptionHandlingBehavior<TRequest, TResponse>> logger)
{
_logger = logger;
}
public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken cancellationToken)
{
try
{
return await next();
}
catch (Exception ex)
{
// Log the exception details
_logger.LogError(ex, "An error occurred while handling request {Request}", typeof(TRequest).Name);
// Return a custom response indicating an error occurred
return CreateErrorResponse<TResponse>("An unexpected error occurred. Please try again later.");
}
}
private TResponse CreateErrorResponse<TResponse>(string errorMessage)
{
// You can customize this method to return a specific type of error response
// For example, a DTO object with error details
var errorResponse = new
{
Success = false,
ErrorMessage = errorMessage
};
// Assuming TResponse can be implicitly cast or converted to the type of errorResponse
// Replace the return statement below with the appropriate casting logic
return (TResponse)Convert.ChangeType(errorResponse, typeof(TResponse));
}
}services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ExceptionHandlingBehavior<,>));
public class SpecificRequest : IRequest<string>
{
public int Number { get; set; }
}
public class SpecificRequestHandler : IRequestHandler<SpecificRequest, string>
{
public Task<string> Handle(SpecificRequest request, CancellationToken cancellationToken)
{
if (request.Number < 0)
throw new ArgumentOutOfRangeException("Number cannot be negative!");
return Task.FromResult($"Number is {request.Number}");
}
}public class SpecificRequestExceptionHandler : IRequestExceptionHandler<SpecificRequest, string>
{
public Task Handle(SpecificRequest request, Exception exception, RequestExceptionHandlerState<string> state, CancellationToken cancellationToken)
{
if (exception is ArgumentOutOfRangeException)
{
state.SetHandled("The provided number is invalid.");
}
else
{
state.SetHandled("An unexpected error occurred.");
}
return Task.CompletedTask;
}
}services.AddTransient(typeof(IRequestExceptionHandler<,>), typeof(SpecificRequestExceptionHandler));
public class LoggingBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
{
private readonly ILogger<LoggingBehavior<TRequest, TResponse>> _logger;
public LoggingBehavior(ILogger<LoggingBehavior<TRequest, TResponse>> logger)
{
_logger = logger;
}
public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken cancellationToken)
{
_logger.LogInformation($"Handling request: {typeof(TRequest).Name}");
try
{
var response = await next();
_logger.LogInformation($"Handled request: {typeof(TRequest).Name}");
return response;
}
catch (Exception ex)
{
_logger.LogError(ex, $"Exception in {typeof(TRequest).Name}: {ex.Message}");
throw; // Rethrow exception to be handled by IRequestExceptionHandler if not handled here
}
}
}public class MyRequestExceptionHandler : IRequestExceptionHandler<MyRequest, string, Exception>
{
public Task Handle(MyRequest request, Exception exception, RequestExceptionHandlerState<string> state, CancellationToken cancellationToken)
{
var response = exception switch
{
ArgumentNullException => "Required value is missing.",
InvalidOperationException => "Invalid operation.",
_ => "Unexpected error occurred."
};
state.SetHandled(response);
return Task.CompletedTask;
}
}public class ExceptionHandlingMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<ExceptionHandlingMiddleware> _logger;
public ExceptionHandlingMiddleware(RequestDelegate next, ILogger<ExceptionHandlingMiddleware> logger)
{
_next = next;
_logger = logger;
}
public async Task InvokeAsync(HttpContext context)
{
try
{
await _next(context);
}
catch (Exception ex)
{
_logger.LogError(ex, "An unexpected error occurred.");
context.Response.StatusCode = StatusCodes.Status500InternalServerError;
await context.Response.WriteAsync("An unexpected error occurred.");
}
}
}public interface IRequestExceptionAction<TRequest, TException>
where TRequest : IRequest
where TException : Exception
{
Task Execute(TRequest request, TException exception, CancellationToken cancellationToken);
}public class CriticalExceptionAction : IRequestExceptionAction<MyRequest, InvalidOperationException>
{
public async Task Execute(MyRequest request, InvalidOperationException exception, CancellationToken cancellationToken)
{
// Log the exception
Console.WriteLine($"Critical exception for request {request.Id}: {exception.Message}");
// Send an SMS or email to the administrator
await SendCriticalAlertAsync(exception);
// Custom logic can be added here
}
private Task SendCriticalAlertAsync(Exception exception)
{
// Simulate sending an SMS or email
Console.WriteLine("Sending critical alert to the admin...");
return Task.CompletedTask;
}
}services.AddMediatR(cfg =>
{
cfg.AddRequestExceptionAction<CriticalExceptionAction>();
});