Reputation: 3899
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 (i.e. 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?
Upvotes: 1
Views: 309
Reputation: 451
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 (e.f. 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.
Upvotes: 2