Skip to content
This repository was archived by the owner on Dec 14, 2018. It is now read-only.
This repository was archived by the owner on Dec 14, 2018. It is now read-only.

RC1: Model validation reports invalid models as valid when an a controller action contains a CancellationToken parameter #3743

@bgeihsgt

Description

@bgeihsgt

We were upgrading our project to RC1 and noticed model validation stopped working after the upgrade. The short version is that having a CancellationToken in your action method will cause all models to get marked with ModelValidationState.Skipped.

Here's a simple setup that demonstrates the problem:

    [Route("api/foo")]
    public class FooController : Controller
    {
        [Route("with-cancellation")]
        [HttpPost]
        public async Task<IActionResult> CreateWithCancellation([FromBody] Foo foo, CancellationToken cancellationToken)
        {
            if (!ModelState.IsValid)
                return new BadRequestResult();

            await DoStuff();

            return Ok(foo);
        }

        [Route("without-cancellation")]
        [HttpPost]
        public async Task<IActionResult> CreateWithoutCancellation([FromBody] Foo foo)
        {
            if (!ModelState.IsValid)
                return new BadRequestResult();

            await DoStuff();

            return Ok(foo);
        }

        public Task DoStuff()
        {
            return Task.FromResult(0);
        }
    }

    public class Foo
    {
        [Required]
        public string requiredProperty { get; set; }
    }

If you post an invalid model to the without-cancellation, it will properly return a 400. If you post an invalid model to with-cancellation it will return a 200 with an invalid model.

After debugging through the problem, we found that the ValidationVisitor for the cancellation token was overwriting all of the model validation results to ModelValidationResult.Skipped.

SuppressValidation rightly gets called for CancellationToken, but it wrongly suppresses it for all entries because the CancellationToken entry has a key of string.Empty. We dug into why this was key was empty we found that the CompositeModelBinder.CreateNewBindingContext is the culprit (see https://github.com/aspnet/Mvc/blob/6.0.0-rc1/src/Microsoft.AspNet.Mvc.Core/ModelBinding/CompositeModelBinder.cs#L101-L168).

In the CancellationToken case, it is falling through to the else block which sets ModelName to string.Empty when it really should be the old binding context's name.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions