Search code examples
c#asp.net-core-mvcasp.net-core-3.1nullable-reference-types

Why is this view-model string field treated as required?


I have a simple login page with the view model below. It used to work just fine, but seemingly suddenly, ModelState will contain an error for the ReturnUrl field if it wasn't supplied to the action. It states, "The ReturnUrl field is required."

public class LoginViewModel
{
    [Required(ErrorMessage = TextValidation.RequiredErrorMessage)]
    [Display(Name = "User")]
    public string Username { get; set; }

    [Required(ErrorMessage = TextValidation.RequiredErrorMessage)]
    [DataType(DataType.Password)]
    [Display(Name = "Password")]
    public string Password { get; set; }

    public string ReturnUrl { get; set; }
}

Why would it do that for a string field (i.e. nullable) that does not have a [Required] annotation?

Here's the beginning of the controller action that makes use of that model.

[HttpPost]
[AllowAnonymous]
public async Task<IActionResult> Login(LoginViewModel model)
{
    if (!ModelState.IsValid)
    {
        return View();  // <-- It returns here.
    }
    ...
}

If I debug, I see the following:

ModelState.Results[2].Key = "ReturnUrl"
ModelState.Results[2].Value.AttemptedValue = null
ModelState.Results[2].Value.Errors[0].ErrorMessage = "The ReturnUrl field is required."

One more detail I can add is that I have the same model and action in another project, and in that project, it works fine. If I debug the Login action in that project, I see that ModelState.Results only contains 2 elements: "Username" and "Password". The third element ("ReturnUrl") is not there, which is what I would expect since it shouldn't be getting validated.


Solution

  • Apparently, reference types had been set to non-nullable for the project. I don't know how it happened, but in <Project>.csproj, there was this entry:

    <Nullable>annotations</Nullable>
    

    Here's more info on the 4 options for that field:
    https://learn.microsoft.com/en-us/dotnet/csharp/nullable-references

    If you delete the entry, you get the default: disable. And note that these nullability contexts can also be controlled at the file or line level via #nullable <context>. The above link covers that as well.

    If I had to guess, I'd say I must have misclicked while viewing Visual Studio's graphical interface for the project's properties:

    properties