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

Commit

Permalink
The model state keys for body bound models which are bound at propert…
Browse files Browse the repository at this point in the history
…y will use the entire model name with this change for example

Consider

public class Person
{
    [FromBody]
    public Address Address { get; set; }
}

public class Address
{
   [Required]
   public string Street { get; set; }

   public int Zip { get; set; }
}

Request body { "Zip" : 12345 }
In this case the error key would be "prefix.Address.Street" (assuming there is a prefix because of additional metadata/positioning for/of the Person model).

public class Person
{
       [Required]
       public string Name { get; set; }
}

public void Action([FromBody]Person p)
{
}
Request body { }
In this case the prefix gets ignored and the error key is Name.
Please note this is so that we are compatible with MVC 5.0

public class Person
{
       [Required]
       public string Name { get; set; }
}

public void Action([FromBody][ModelBinder(Name = "prefix")] Person p)
{
}

public void Action2([FromBody][Bind(Name = "prefix")] Person p)
{
}
Request body { }
In both these cases (Action and Action2) the prefix gets ignored and the error key is Name.
This is a slight improvement from mvc, as in MVC the action parameter would be null.

The followup for this would be to fix #2416 -
This PR ignores the validation assuming that #2416 will address the issues and update the test.

NOTE: previous versions of mvc did not have property binding and hence there is no precedence in this case. For MVC and Web API it was possible to body bind an action parameter which used an empty prefix instead of a parameter name for adding errors to model state (In case of MVC if a custom prefix was provided, it failed binding from body i.e the parameter was null).
  • Loading branch information
harshgMSFT committed Apr 22, 2015
1 parent 323ec2f commit ec21283
Show file tree
Hide file tree
Showing 11 changed files with 476 additions and 28 deletions.
17 changes: 16 additions & 1 deletion Mvc.sln
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.22726.0
VisualStudioVersion = 14.0.22810.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{DAAE4C74-D06F-4874-A166-33305D2643CE}"
EndProject
Expand Down Expand Up @@ -160,6 +160,8 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.JsonPatch.
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.JsonPatch", "src\Microsoft.AspNet.JsonPatch\Microsoft.AspNet.JsonPatch.xproj", "{4D55F4D8-633B-462F-A5B1-FEB84BD2D534}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.IntegrationTests", "test\Microsoft.AspNet.IntegrationTests\Microsoft.AspNet.IntegrationTests.xproj", "{864FA09D-1E48-403A-A6C8-4F079D2A30F0}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -950,6 +952,18 @@ Global
{4D55F4D8-633B-462F-A5B1-FEB84BD2D534}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{4D55F4D8-633B-462F-A5B1-FEB84BD2D534}.Release|x86.ActiveCfg = Release|Any CPU
{4D55F4D8-633B-462F-A5B1-FEB84BD2D534}.Release|x86.Build.0 = Release|Any CPU
{864FA09D-1E48-403A-A6C8-4F079D2A30F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{864FA09D-1E48-403A-A6C8-4F079D2A30F0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{864FA09D-1E48-403A-A6C8-4F079D2A30F0}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{864FA09D-1E48-403A-A6C8-4F079D2A30F0}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{864FA09D-1E48-403A-A6C8-4F079D2A30F0}.Debug|x86.ActiveCfg = Debug|Any CPU
{864FA09D-1E48-403A-A6C8-4F079D2A30F0}.Debug|x86.Build.0 = Debug|Any CPU
{864FA09D-1E48-403A-A6C8-4F079D2A30F0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{864FA09D-1E48-403A-A6C8-4F079D2A30F0}.Release|Any CPU.Build.0 = Release|Any CPU
{864FA09D-1E48-403A-A6C8-4F079D2A30F0}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{864FA09D-1E48-403A-A6C8-4F079D2A30F0}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{864FA09D-1E48-403A-A6C8-4F079D2A30F0}.Release|x86.ActiveCfg = Release|Any CPU
{864FA09D-1E48-403A-A6C8-4F079D2A30F0}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -1028,5 +1042,6 @@ Global
{B42D4844-FFF8-4EC2-88D1-3AE95234D9EB} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
{81C20848-E063-4E12-AC40-0B55A532C16C} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1}
{4D55F4D8-633B-462F-A5B1-FEB84BD2D534} = {32285FA4-6B46-4D6B-A840-2B13E4C8B58E}
{864FA09D-1E48-403A-A6C8-4F079D2A30F0} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1}
EndGlobalSection
EndGlobal
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,41 @@ await PopulateArgumentsAsync(
return actionArguments;
}

public async Task<ModelBindingResult> BindModelAsync(
ParameterDescriptor parameter,
ModelStateDictionary modelState,
OperationBindingContext operationContext)
{
var metadata = _modelMetadataProvider.GetMetadataForType(parameter.ParameterType);
var parameterType = parameter.ParameterType;
var modelBindingContext = GetModelBindingContext(
parameter.Name,
metadata,
parameter.BindingInfo,
modelState,
operationContext);

var modelBindingResult = await operationContext.ModelBinder.BindModelAsync(modelBindingContext);
if (modelBindingResult != null && modelBindingResult.IsModelSet)
{
var key = modelBindingResult.Key;
var modelExplorer = new ModelExplorer(
_modelMetadataProvider,
metadata,
modelBindingResult.Model);

var validationContext = new ModelValidationContext(
key,
modelBindingContext.BindingSource,
operationContext.ValidatorProvider,
modelState,
modelExplorer);
_validator.Validate(validationContext);
}

return modelBindingResult;
}

private void ActivateProperties(object controller, Type containerType, Dictionary<string, object> properties)
{
var propertyHelpers = PropertyHelper.GetProperties(controller);
Expand All @@ -88,31 +123,10 @@ private async Task PopulateArgumentsAsync(
{
foreach (var parameter in parameterMetadata)
{
var metadata = _modelMetadataProvider.GetMetadataForType(parameter.ParameterType);
var parameterType = parameter.ParameterType;
var modelBindingContext = GetModelBindingContext(
parameter.Name,
metadata,
parameter.BindingInfo,
modelState,
operationContext);

var modelBindingResult = await operationContext.ModelBinder.BindModelAsync(modelBindingContext);
var modelBindingResult = await BindModelAsync(parameter, modelState, operationContext);
if (modelBindingResult != null && modelBindingResult.IsModelSet)
{
var modelExplorer = new ModelExplorer(
_modelMetadataProvider,
metadata,
modelBindingResult.Model);

arguments[parameter.Name] = modelBindingResult.Model;
var validationContext = new ModelValidationContext(
modelBindingResult.Key,
modelBindingContext.BindingSource,
operationContext.ValidatorProvider,
modelState,
modelExplorer);
_validator.Validate(validationContext);
}
}
}
Expand Down
9 changes: 7 additions & 2 deletions src/Microsoft.AspNet.Mvc.Core/ModelBinders/BodyModelBinder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,13 @@ protected async override Task<ModelBindingResult> BindModelCoreAsync([NotNull] M
{
var model = await formatter.ReadAsync(formatterContext);

// key is empty to ensure that the model name is not used as a prefix for validation.
return new ModelBindingResult(model, key: string.Empty, isModelSet: true);
var isTopLevelObject = bindingContext.ModelMetadata.ContainerType == null;

// For compatibility with MVC 5.0 for top level object we want to consider an empty key instead of
// the parameter name/a custom name. In all other cases (like when binding body to a property) we
// consider the entire ModelName as a prefix.
var modelBindingKey = isTopLevelObject ? string.Empty : bindingContext.ModelName;
return new ModelBindingResult(model, key: modelBindingKey, isModelSet: true);
}
catch (Exception ex)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ private bool ValidateProperties(

var propertyBindingName = propertyMetadata.BinderModelName ?? propertyMetadata.PropertyName;
var childKey = ModelBindingHelper.CreatePropertyModelName(currentModelKey, propertyBindingName);

if (!ValidateNonVisitedNodeAndChildren(
childKey,
propertyValidationContext,
Expand Down
Loading

0 comments on commit ec21283

Please sign in to comment.