简体   繁体   中英

How do I implement data validation attributes on a list of derived types in Asp Net Core 3.1 Web API

Say I have a base class 'animal' and a derived class 'dog' which has a new property (numberOfPaws).

If I create a controller which accepts a list of 'animals' then I expect that the data validation attributes that I have labelled on my 'dog' class will be validated against in the usual way. I expect them to be rejected if out of range. However, though this works on an individual 'animal' it does not work in a list / array of 'animals'.

Is there a way to make this work for a list or do I have to write some bespoke code in the controller?

Thank you for any insights, please see the code below.

public enum AnimalType
{
    Dog = 0,
    Cat = 1,
}

public class Animal
{
    [Required]
    public string Name {get ; set;}
    
    public AnimalType Type {get ;set;}
}

public class Dog : Animal
{
    [Required]
    [Range(0, 4)] // hopefully 4 <3
    public int NumberOfPaws{get ; set;}
}


[HttpPost("/animal/setAnimal")]
[AllowAnonymous]
public IActionResult SetAnimal([FromBody] Animal animal)
{
    _logger.LogInformation("Send a dog with 5 paws here and observe it is rejected correctly");
    return Ok();
}

[HttpPost("/animal/setAnimalList")]
[AllowAnonymous]
public IActionResult SetAnimalList([FromBody] List<Animal> animalList)
{
    _logger.LogInformation("Send a list of dogs here and observe that although they are correctly deseriliazed they are allowed through with 5 paws :(");

    return Ok();
}

Note that i am using JsonSubTypes ( https://github.com/manuc66/JsonSubTypes ) in order to correctly serialize and deserialize the derived types via the AnimalType attribute in the 'animal' base class.

I am happy to put a minimum repeatable example on GitHub if anyone is interested in observing this.

I eventually found a solution to this, it's not what I would call 'clean' but it does seem to work so I hope it helps others who end up here, let me know if you find a better solution.


If you create a custom data attribute and override the IsValid() method then you can validate the inherited types properties using a switch statement on the type which identifies your derived type.

Put this attribute on your base class (Animal) and the validation will work returning the standard ASP.net core validationProblemDetails object.

Note** You might be tempted not to bother with implementing validation on each inherited property and instead just validate the whole inherited object. I tried this but got stuck in an infinite validation loop.

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

namespace Animal.Circus
{

    [AttributeUsage(AttributeTargets.Class)]
    public class AnimalAttribute : ValidationAttribute
    {
        // Not sure this is the perfect implementation of inhertited type validation but it works for now

        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            var animal = value as Animal;
            var success = true;

            var validationResults = new List<ValidationResult>();


            switch (animal.Type)
            {
                case AnimalType.Dog:
                    {
                        var dog = (Dog)animal;
                        var ctx = new ValidationContext(dog);
                        ctx.MemberName = nameof(dog.NumberOfPaws);
                        success = success & Validator.TryValidateProperty(dog.NumberOfPaws, ctx, validationResults);
                    }
                    break;
                case AnimalType.Animal:
                    // already validated by default
                    break;
                default:
                    break;
            }

            if (!success)
            {
                return new ValidationResult(validationResults[0].ErrorMessage, validationResults[0].MemberNames);
            }
            else
            {
                return ValidationResult.Success;
            }
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM