Search code examples
asp.net-core-mvcasp.net-core-webapi.net-6.0fluentvalidation

Fluent Validation and ASP.NET Core 6 Web API


After following the FluentValidation website, I was able to successfully implement Fluent Validation.

However, my response body looks very different and contains a lot of information. Is it possible to have a regular response body with validation errors?

I will put down the steps I took to implement Fluent Validation. Your advice and help are much appreciated. I am using Manual Validation because based on the Fluent Validation website they are not supporting the Automatic Validation anymore.

In the Program file, I added:

builder.Services.AddValidatorsFromAssemblyContaining<CityValidator>();

Then I added a class that validated my City class which has two properties Name and Description :

public class CityValidator : AbstractValidator<City>
{
    public CityValidator()
    {
        RuleFor(x => x.Name)
                .NotNull()
                .NotEmpty()
                .WithMessage("Please specify a name");
        RuleFor(x => x.Description)
                .NotNull()
                .NotEmpty()
                .WithMessage("Please specify a Description");
    }
}

In my CitiesController constructor I injected Validator<City> validator and in my Action method, I am using this code:

ValidationResult result = await _validator.ValidateAsync(city);

if (!result.IsValid)
{
    result.AddToModelState(this.ModelState);
    return BadRequest(result);
}

The AddToModelState is an extension method:

public static void AddToModelState(this ValidationResult result, ModelStateDictionary modelState)
{
    if (!result.IsValid)
    {
        foreach (var error in result.Errors)
        {
            modelState.AddModelError(error.PropertyName, error.ErrorMessage);
        }
    }
}

On POST, I am getting the response as:

{
    "isValid": false,
    "errors": [
        {
            "propertyName": "Name",
            "errorMessage": "Please specify a name",
            "attemptedValue": "",
            "customState": null,
            "severity": 0,
            "errorCode": "NotEmptyValidator",
            "formattedMessagePlaceholderValues": {
                "PropertyName": "Name",
                "PropertyValue": ""
            }
        },
        {
            "propertyName": "Description",
            "errorMessage": "Please specify a name",
            "attemptedValue": "",
            "customState": null,
            "severity": 0,
            "errorCode": "NotEmptyValidator",
            "formattedMessagePlaceholderValues": {
                "PropertyName": "Description",
                "PropertyValue": ""
            }
        }
    ],
    "ruleSetsExecuted": [
        "default"
    ]
}

While the regular response without Fluent Validation looks like this:

{
    "errors": {
        "": [
            "A non-empty request body is required."
        ],
        "pointofInterest": [
            "The pointofInterest field is required."
        ]
    },
    "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
    "title": "One or more validation errors occurred.",
    "status": 400,
    "traceId": "00-1a68c87bda2ffb8de50b7d2888b32d02-94d30c7679aec10b-00"
}

The question: Is there a way to use Fluent Validation and get the response format like:

{
    "errors": {
        "": [
            "A non-empty request body is required."
        ],
        "pointofInterest": [
            "The pointofInterest field is required."
        ]
    },
    "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
    "title": "One or more validation errors occurred.",
    "status": 400,
    "traceId": "00-1a68c87bda2ffb8de50b7d2888b32d02-94d30c7679aec10b-00"
}

Solution

  • Updated ans:

    with your code, you can simply replace.

     return BadRequest(result); // replace this line with below line.
     return ValidationProblem(ModelState);
    

    then you get same format as required.

    enter image description here

    ------------------------*----------------------------------------

    Please ignore this for manual validation.

    You don't need explicit validation call.

    this code is not required:

    ValidationResult result = await _validator.ValidateAsync(city);
    
    if (!result.IsValid)
    {
        result.AddToModelState(this.ModelState);
        return BadRequest(result);
    }
    

    it will auto validate the model using your custom validator.

    you simply need this

    if (!ModelState.IsValid)
    {
       return BadRequest(ModelState);
    }
    

    and it will give you errors in the require format.