Skip to content

Replacing IExceptionFilter #108

Description

@zoinkydoink

I am using mediatr to throw ValidationException once it finds errors, then in my API project I have a global exception handler that catches these and returns them to the caller, also it catches NotFound.
I feel like some of the code in my exception handler might be reduntant so I would like to make it as clean as possible.

I would like to achieve the following

  1. Continue to use Mediatr to trap validation errors and throw ValidationException when it finds them
  2. Wrap my responses so there is a "result" property when its successful (autowrapper does this already)
  3. When there are validation errors, show them as a list (autowrapper does this but I couldnt get it to work with my code/global exception handler
  4. for my NotFoundException (custom), return the message in the exception and return 404

Should I be throwing ApiException inside my globalexception handler? can global exception handler be completely removed and still be able to trap validation exception from mediatr and trap NotFound items and return a 404,

sorry for the confusion, I simple just want to add the functionality of AutoWrapper to my existing code to get the whole wrapping to my result, either good or bad returns

Global Exception Handler

public class HttpGlobalExceptionFilter : IExceptionFilter
 {
     private readonly IWebHostEnvironment _env;
     private readonly ILogger<HttpGlobalExceptionFilter> _logger;

     public HttpGlobalExceptionFilter(IWebHostEnvironment env, ILogger<HttpGlobalExceptionFilter> logger)
     {
         _env = env;
         _logger = logger;
     }

     public void OnException(ExceptionContext context)
     {
         var msg = context.Exception.Message;

         switch (context.Exception)
         {
             case ValidationException ex:
             {
                
                 context.HttpContext.Response.ContentType = "application/json";
                 context.HttpContext.Response.StatusCode = (int) HttpStatusCode.BadRequest;
                 context.Result = new JsonResult(ex.Errors);
                 msg = string.Empty;
                 foreach (var error in ex.Errors)
                     msg += $"{error.ErrorMessage}: {error.AttemptedValue}" + Environment.NewLine;
                 break;
             }
             case NotFoundException:
                 msg = context.Exception.Message;
                 context.Result = new NotFoundError(context.Exception.Message);
                 context.HttpContext.Response.StatusCode = (int) HttpStatusCode.NotFound;
                 break;
             default:
             {
                 var json = new JsonErrorResponse
                 {
                     Messages = new[] {"An error occur.Try it again."}
                 };

                 if (_env.IsDevelopment()) json.DeveloperMessage = context.Exception.ToString();


                 context.Result = new InternalServerErrorObjectResult(json);
                 context.HttpContext.Response.StatusCode = (int) HttpStatusCode.InternalServerError;
                 break;
             }
         }

         _logger.LogError(new EventId(context.Exception.HResult), context.Exception, msg);
         context.ExceptionHandled = true;
     }

     private class NotFoundError : ObjectResult
     {
         public NotFoundError(object error)
             : base(error)
         {
             StatusCode = StatusCodes.Status404NotFound;
         }
     }

     private class JsonErrorResponse
     {
         public string[] Messages { get; set; }

         public string DeveloperMessage { get; set; }
     }

     public class InternalServerErrorObjectResult : ObjectResult
     {
         public InternalServerErrorObjectResult(object error)
             : base(error)
         {
             StatusCode = StatusCodes.Status500InternalServerError;
         }
     }
 }

Mediatr Validation Behaviour

    public class ValidationBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
        where TRequest : IRequest<TResponse>
    {
        private readonly IEnumerable<IValidator<TRequest>> _validators;

        public ValidationBehavior(IEnumerable<IValidator<TRequest>> validators)
        {
            _validators = validators;
        }

        public Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken,
            RequestHandlerDelegate<TResponse> next)
        {
            var context = new ValidationContext<TRequest>(request);
            var failures = _validators
                .Select(v => v.Validate(context))
                .SelectMany(result => result.Errors)
                .Where(f => f != null)
                .ToList();

            if (failures.Count != 0) throw new ValidationException(failures);

            return next();
        }
    }

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions