I have a controller that takes some model MyModel
in a POST method. Inside of the controller method, there is a check:
[HttpPost("mymodel")]
public async Task<IActionResult> DoStuff(MyModel model)
{
if (!ModelState.IsValid)
{
// early return with errors
}
// ...
}
With some specific data, ModelState.IsValid == false
and ModelState.ErrorCount == 0
. The reason why it is invalid, is that some properties end up with ValidationState == Unvalidated
, making the whole model Unvalidated
, thus not valid.
How can I find out what happens? Why would some properties be just Unvalidated
instead of having an error? Some of the Unvalidated
properties have validation attributes, some don't, but other properties that end up as valid also follow the same pattern - some have attributes, some don't.
Any help with a digging direction would appreciated.
It turns out, that in our case the properties in Unvalidated
state were defined in base class, some of them were get-only. When we called the endpoint, the POST data had values for the get only properties, because model came from GET endpoint.
Now, in the UI we removed one element from a list, making the previously calculated values out of date. The difference was cause of the error - it went away when stopped sending the properties.
I tried to create a repro, but it works on minimal example, and I wasted enough time on that. Maybe someone will find that helpful. Here is what I thought would recreate the problem:
public class Class1
{
public List<Class2> Objects { get; set; } = new();
public List<Class2> Objects2 => Objects.ToList();
public IEnumerable<int> ObjectsIds => Objects.Select(x => x.Id);
}
public class Class2
{
public int Id { get; set; }
}
public class SampleController : Controller
{
[HttpPost("/post")]
public ActionResult Post(Class1 arg)
{
if (!ModelState.IsValid) // in our code this was invalid
{
return ValidationProblem();
}
return Ok();
}
}
[Fact]
public async Task Test1()
{
var application = new WebApplicationFactory<Program>();
var client = application.CreateClient();
var value = new
{
Objects = new[]
{
new { Id = 1 },
new { Id = 2 },
new { Id = 2 },
},
Objects2 = new[]
{
new { Id = 1 },
new { Id = 2 },
},
ObjectsIds = new[] { 1, 2, 3 }
};
var response = await client.PostAsJsonAsync("/post", value);
response.StatusCode.Should().Be(HttpStatusCode.OK);
}
The test is green though, and I have no idea why it fails in our case. Both are dotnet 6. I tried putting the get-only properties in derived class, deeper nesting, minimal example still works.
Have a nice day!