4

I'm using FluentValidation to validate inputs for my ASP.NET Core 3.1 Web API. I'm doing it with the "automatic" way. So in my Startup class I have this:

        services.AddControllers()
            .AddFluentValidation(opt =>
            {
                opt.RegisterValidatorsFromAssemblyContaining(typeof(UserInputValidator));
            });

This way, FluentValidation magically validates all inputs from the above assembly, with no other code needed. Pretty sweet, but... the response format, is different than response format I use when I return BadRequest. This is FluentValidation format:

{
    "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
    "title": "One or more validation errors occurred.",
    "status": 400,
    "traceId": "|3b037c98-40d28ab8553d6989.",
    "errors": {
        "UserId": [
            "'User Id' must be greater than '0'."
        ],
        "Score": [
            "'Score' must be between 0 and 500. You entered 900."
        ]
    }
}

I would rather have:

{
    "code": 123456,
    "message": "One or more validation errors occurred.",
    "errors": [
        "'User Id' must be greater than '0'.",
        "'Score' must be between 0 and 500. You entered 900."
    ]
}

So my question is, Is it possible to override it?

I have one idea, but not sure if it's a good practice (or even possible), but if FluentValidation instead of returning its BadRequest response would throw an exception, I would be able to catch it in the middleware and reformat.

In any event, FluentValidation is very popular, .NET Core WebAPI is very popular. It's hard for me to believe that "ninja developers" would just use response provided by thrid-party tool, that doesn't match their response format. Yet, I don't see many materials how to fix it (even on the FluentValidation website. Did I miss it?

Ish Thomas
  • 2,270
  • 2
  • 27
  • 57

1 Answers1

7

You can achieve this result by using Filters in asp.net core. After you have completed setting up Fluent Validation you have to write a filter.

 public class CustomValidationAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext context)
        {
            if (!context.ModelState.IsValid)
            {
                var errors = context.ModelState.Values.Where(v => v.Errors.Count > 0)
                        .SelectMany(v => v.Errors)
                        .Select(v => v.ErrorMessage)
                        .ToList();

                var responseObj = new
                {                        
                    Code= 123456,
                    Message = "One or more validation errors occurred.",
                    Errors = errors
                };

                context.Result = new JsonResult(responseObj)
                {
                    StatusCode = 200
                };
            }
        }
    }

And in the startup class define this filter.

services.AddMvc(options =>
            {
                options.Filters.Add(typeof(CustomValidationAttribute));
            })
            .AddFluentValidation(fvc => fvc.RegisterValidatorsFromAssemblyContaining<AnyValidatorClass>());

            services.Configure<ApiBehaviorOptions>(options =>
            {
                options.SuppressModelStateInvalidFilter = true;
            });
Syed A.
  • 186
  • 2
  • 13