Removing creating ModelValidationNode by default in CompositeModelBinder.

This moves the responsibility of of MVN creation to individual model binders if they want the individual models to be validated.
This commit is contained in:
Harsh Gupta 2015-05-27 12:16:11 -07:00
parent 3fe0490b99
commit 4f419eee55
13 changed files with 83 additions and 44 deletions

View File

@ -89,12 +89,16 @@ namespace Microsoft.AspNet.Mvc
metadata,
modelBindingResult.Model);
var validationContext = new ModelValidationContext(
modelBindingContext.BindingSource,
operationContext.ValidatorProvider,
modelState,
modelExplorer);
_validator.Validate(validationContext, modelBindingResult.ValidationNode);
if (modelBindingResult.ValidationNode != null)
{
var validationContext = new ModelValidationContext(
modelBindingContext.BindingSource,
operationContext.ValidatorProvider,
modelState,
modelExplorer);
_validator.Validate(validationContext, modelBindingResult.ValidationNode);
}
}
return modelBindingResult;

View File

@ -39,12 +39,16 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
try
{
var model = Convert.FromBase64String(value);
var validationNode = new ModelValidationNode(
bindingContext.ModelName,
bindingContext.ModelMetadata,
model);
// We do not need to set an explict ModelValidationNode since CompositeModelBinder does that automatically.
return new ModelBindingResult(
model,
bindingContext.ModelName,
isModelSet: true);
isModelSet: true,
validationNode: validationNode);
}
catch (Exception ex)
{

View File

@ -17,7 +17,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
if (bindingContext.ModelType == typeof(CancellationToken))
{
var model = bindingContext.OperationBindingContext.HttpContext.RequestAborted;
return Task.FromResult(new ModelBindingResult(model, bindingContext.ModelName, isModelSet: true));
var validationNode =
new ModelValidationNode(bindingContext.ModelName, bindingContext.ModelMetadata, model);
return Task.FromResult(new ModelBindingResult(
model,
bindingContext.ModelName,
isModelSet: true,
validationNode: validationNode));
}
return Task.FromResult<ModelBindingResult>(null);

View File

@ -83,24 +83,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
}
}
// Update the model validation node if the model binding result was set but no validation node was provided.
// This would typically be the case where leaf level model binders, do not have to add a validation node
// for validation to take effect. The composite being the entry point for model binders, takes care or
// adding missing validation nodes.
var modelValidationNode = modelBindingResult.ValidationNode;
if (modelBindingResult.IsModelSet && modelValidationNode == null)
{
modelValidationNode = new ModelValidationNode(
bindingKey,
bindingContext.ModelMetadata,
modelBindingResult.Model);
}
return new ModelBindingResult(
modelBindingResult.Model,
bindingKey,
modelBindingResult.IsModelSet,
modelValidationNode);
modelBindingResult.ValidationNode);
}
private async Task<ModelBindingResult> TryBind(ModelBindingContext bindingContext)

View File

@ -46,7 +46,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
model = new FormCollection(new Dictionary<string, string[]>());
}
return new ModelBindingResult(model, bindingContext.ModelName, isModelSet: true);
var validationNode =
new ModelValidationNode(bindingContext.ModelName, bindingContext.ModelMetadata, model);
return new ModelBindingResult(
model,
bindingContext.ModelName,
isModelSet: true,
validationNode: validationNode);
}
}
}

View File

@ -24,14 +24,26 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
var postedFiles = await GetFormFilesAsync(bindingContext);
var value = postedFiles.FirstOrDefault();
return new ModelBindingResult(value, bindingContext.ModelName, isModelSet: value != null);
var validationNode =
new ModelValidationNode(bindingContext.ModelName, bindingContext.ModelMetadata, value);
return new ModelBindingResult(
value,
bindingContext.ModelName,
isModelSet: value != null,
validationNode: validationNode);
}
else if (typeof(IEnumerable<IFormFile>).GetTypeInfo().IsAssignableFrom(
bindingContext.ModelType.GetTypeInfo()))
{
var postedFiles = await GetFormFilesAsync(bindingContext);
var value = ModelBindingHelper.ConvertValuesToCollectionType(bindingContext.ModelType, postedFiles);
return new ModelBindingResult(value, bindingContext.ModelName, isModelSet: value != null);
var validationNode =
new ModelValidationNode(bindingContext.ModelName, bindingContext.ModelMetadata, value);
return new ModelBindingResult(
value,
bindingContext.ModelName,
isModelSet: value != null,
validationNode: validationNode);
}
return null;

View File

@ -30,12 +30,16 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
newModel = valueProviderResult.ConvertTo(bindingContext.ModelType);
ModelBindingHelper.ReplaceEmptyStringWithNull(bindingContext.ModelMetadata, ref newModel);
var validationNode = new ModelValidationNode(
bindingContext.ModelName,
bindingContext.ModelMetadata,
newModel);
// We do not need to set an explict ModelValidationNode since CompositeModelBinder does that automatically.
return new ModelBindingResult(
newModel,
bindingContext.ModelName,
isModelSet: true);
isModelSet: true,
validationNode: validationNode);
}
catch (Exception ex)
{

View File

@ -19,12 +19,16 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult);
var model = valueProviderResult.RawValue;
ModelBindingHelper.ReplaceEmptyStringWithNull(bindingContext.ModelMetadata, ref model);
var validationNode = new ModelValidationNode(
bindingContext.ModelName,
bindingContext.ModelMetadata,
model);
// We do not need to set an explict ModelValidationNode since CompositeModelBinder does that automatically.
return new ModelBindingResult(
model,
bindingContext.ModelName,
isModelSet: true);
isModelSet: true,
validationNode: validationNode);
}
internal static async Task<ValueProviderResult> GetCompatibleValueProviderResult(ModelBindingContext context)

View File

@ -247,8 +247,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
modelState.GetFieldValidationState(errorKey) == ModelValidationState.Unvalidated)
{
// If we are not able to add a model error
// for instance when the max error count is reached, mark the model as skipped.
// If we are not able to add a model error
// for instance when the max error count is reached, mark the model as skipped.
modelState.MarkFieldSkipped(errorKey);
}

View File

@ -309,7 +309,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
{
if (mbc.ModelType == typeof(int))
{
return Task.FromResult(new ModelBindingResult(42, mbc.ModelName, true));
var model = 42;
var validationNode = new ModelValidationNode(mbc.ModelName, mbc.ModelMetadata, model);
return Task.FromResult(new ModelBindingResult(model, mbc.ModelName, true, validationNode));
}
return Task.FromResult<ModelBindingResult>(null);
});
@ -325,7 +327,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
{
if (mbc.ModelType == typeof(string))
{
return Task.FromResult(new ModelBindingResult("some-value", mbc.ModelName, true));
var model = "some-value";
var validationNode = new ModelValidationNode(mbc.ModelName, mbc.ModelMetadata, model);
return Task.FromResult(new ModelBindingResult(model, mbc.ModelName, true, validationNode));
}
return Task.FromResult<ModelBindingResult>(null);
});

View File

@ -588,10 +588,14 @@ namespace Microsoft.AspNet.Mvc.Core.Test
private static ActionBindingContext GetActionBindingContext(object model)
{
var binder = new Mock<IModelBinder>();
binder
.Setup(b => b.BindModelAsync(It.IsAny<ModelBindingContext>()))
.Returns(Task.FromResult(
result: new ModelBindingResult(model: model, key: string.Empty, isModelSet: true)));
binder.Setup(b => b.BindModelAsync(It.IsAny<ModelBindingContext>()))
.Returns<ModelBindingContext>(mbc =>
{
var validationNode = new ModelValidationNode(string.Empty, mbc.ModelMetadata, model);
return Task.FromResult(
result: new ModelBindingResult(model, string.Empty, isModelSet: true, validationNode: validationNode));
});
return new ActionBindingContext()
{
ModelBinder = binder.Object,

View File

@ -1462,7 +1462,7 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
};
var url = "http://localhost/dealers/43/update-vehicle?dealer.name=TestCarDealer&dealer.location=NE";
// Act
var response = await client.PostAsJsonAsync(url, postedContent);

View File

@ -74,7 +74,9 @@ namespace ModelBindingWebSite.Controllers
// by the type converter binder.
OrderStatus model;
var isModelSet = Enum.TryParse<OrderStatus>("Status" + request.Query.Get("status"), out model);
return Task.FromResult(new ModelBindingResult(model, "status", isModelSet));
var validationNode =
new ModelValidationNode(bindingContext.ModelName, bindingContext.ModelMetadata, model);
return Task.FromResult(new ModelBindingResult(model, "status", isModelSet, validationNode));
}
return Task.FromResult<ModelBindingResult>(null);
@ -91,15 +93,17 @@ namespace ModelBindingWebSite.Controllers
model.BinderType = GetType();
var key =
string.IsNullOrEmpty(bindingContext.ModelName) ?
"productId" :
var key =
string.IsNullOrEmpty(bindingContext.ModelName) ?
"productId" :
bindingContext.ModelName + "." + "productId";
var value = await bindingContext.ValueProvider.GetValueAsync(key);
model.ProductId = (int)value.ConvertTo(typeof(int));
return new ModelBindingResult(model, key, true);
var validationNode =
new ModelValidationNode(bindingContext.ModelName, bindingContext.ModelMetadata, value);
return new ModelBindingResult(model, key, true, validationNode);
}
return null;