简体   繁体   中英

DataAnnotations Validator Class Not Replicating Implicit Required Check for Non-Nullable String

When using DataAnnotations to validate models for incoming controller requests, non-nullable reference types are implicitly treated as required unless you declare them as nullable (ie string? instead of string ).

public class MyExampleModel
{
    // Cannot be null! Equivalent to having [Required(AllowEmptyStrings = true)] on it
    public string PropertyName { get; set; } 

    // Allowed to be null! No validation errors occur.
    public string? OtherProp { get; set; } 
}

This behavior results in an expected validation error of The PropertyName field is required when hitting the endpoint via Postman / etc.

However, when using the Validator class in a unit testing scenario, this implicit check is not correctly reported when passing null for the string PropertyName property.

using System.ComponentModel.DataAnnotations;
using FluentAssertions;
using Xunit;

namespace MyNamespace.Tests;

public class TestingValidationOfMyExampleModel
{
    [Fact]
    public void ShouldHaveErrorWhenPropertyNameIsNull()
    {
        var model = new MyExampleModel(); // model.PropertyName is null.

        var validationResults = new List<ValidationResult>();
        var validationContext = new ValidationContext(model, null, null);

        // No errors returned! validationResults remains empty.
        Validator.TryValidateObject(model, validationContext, validationResults, true);

        validationResults.Should().HaveCount(1); // Fails!
    }
}

Is there some way to configure the static System.ComponentModel.DataAnnotations.Validator class so that it also makes this implicit check?

The DataAnnotations.Validator will validate exclusively based on the applied data annotation attributes. It's has been around for a very long time, and it's heavily used by various frameworks/tools (ef EF, Winforms, MVC). Modifying the behavior would break everything.

On the other hand, the ASP.NET Core team decided to add support for NRT in the MVC's model binding and validation middleware. They did it by just adding the Required attributes to the non-nullable reference types. You can find the implementation here DataAnnotationsMetadataProvider . Here is the section

...
if (addInferredRequiredAttribute)
{
    // Since this behavior specifically relates to non-null-ness, we will use the non-default
    // option to tolerate empty/whitespace strings. empty/whitespace INPUT will still result in
    // a validation error by default because we convert empty/whitespace strings to null
    // unless you say otherwise.
    requiredAttribute = new RequiredAttribute()
    {
        AllowEmptyStrings = true,
    };
    attributes.Add(requiredAttribute);
}
...

You can opt-out of this behavior while registering the controllers

builder.Services.AddControllers(options => options.SuppressImplicitRequiredAttributeForNonNullableReferenceTypes = true);

The ability to opt out makes it OK to have such inferring in MVC.

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