In ASP.NET Core Web API, how do I find which data annotation(s) failed for a property in ModelState

372 Views Asked by At

I have the following model defined

public class User
{
    [EmailAddress(ErrorMessage = "The Email Address is not valid.")]
    [Required(ErrorMessage = "Please enter an email address.")]
    [StringLength(5, ErrorMessage = "The Email Address must be less than 5 characters.")]
    [Display(Name = "Email Address:")]
    public string EmailAddress { get; set; }
}

Now, when the incoming http post request has following payload, which doesn't satisfy two data annotations such as StringLength and EmailAddress, how do I find out which data annotations failed for the property EmailAddress?

{
    "EmailAddress": "Raj.xyz.com"
}

I could not find anything in ModelState which would let me know what are the data annotations failed.

1

There are 1 best solutions below

0
Vlad DX On

If you run validation by yourself using System.ComponentModel.DataAnnotations.Validator.

Then your code would look like:

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;

var user = new User()
{
    EmailAddress = "Raj.xyz.com"
};

var validationResults = new List<ValidationResult>();
Validator.TryValidateObject(user, new ValidationContext(user), validationResults, true);

var errors = validationResults
    .Select(x => $"Members: '{string.Join("', ", x.MemberNames)}'\nError: {x.ErrorMessage}\n");

Console.WriteLine(string.Join("\n", errors));

public class User
{
    [EmailAddress(ErrorMessage = "The Email Address is not valid.")]
    [Required(ErrorMessage = "Please enter an email address.")]
    [StringLength(5, ErrorMessage = "The Email Address must be less than 5 characters.")]
    [Display(Name = "Email Address:")]
    public string EmailAddress { get; set; }
}

Check out the result: https://dotnetfiddle.net/Ndgzmp


The code prints the output to the console:

Members: 'EmailAddress' Error: The Email Address is not valid.

Members: 'EmailAddress' Error: The Email Address must be less than 5 characters.


As you can see, you pass a List<ValidationResult> to the Validator. It fills the list with validation errors.

ValidationResult instance has:

  • .MemberNames property that indicates the property names which failed validation,
  • .ErrorMessage property that provides a human-readable error that can be shown to an end-user.

If you do it in Web API, you need:

  1. Create your validation filter and get access to the ModelState:

    public class ValidationFilter : IActionFilter
    {
        public void OnActionExecuting(ActionExecutingContext context)
        {
            if (!context.ModelState.IsValid)
            {
                // Here you have access to the `ModelState`
                context.Result = new UnprocessableEntityObjectResult(context.ModelState);
            }
        }
    
        public void OnActionExecuted(ActionExecutedContext context)
        {
        }
    }
    
  2. Add DI registrations:

    // Register your validation filter in the DI
    builder.Services.AddScoped<ValidationFilter>();
    // Disable implicit validation
    builder.Services.Configure<ApiBehaviorOptions>(options => options.SuppressModelStateInvalidFilter = true);
    
  3. Put filter on your controller:

     ServiceFilter(typeof(ValidationFilter))]
    public class ProductsController : ControllerBase
    {
        [HttpPost(Name = "Post")]
        public HttpResponseMessage Post([FromBody] Product product)
        {
            return new HttpResponseMessage(HttpStatusCode.OK);
        }
    }