Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replacing IExceptionFilter #108

Open
zoinkydoink opened this issue Jun 11, 2021 · 0 comments
Open

Replacing IExceptionFilter #108

zoinkydoink opened this issue Jun 11, 2021 · 0 comments

Comments

@zoinkydoink
Copy link

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();
        }
    }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant