Adding support for property level binding using IBinderMetadata and enabling FromXXX attributes to be decorated on properties.
This commit is contained in:
parent
27432d9453
commit
d00c7ef597
|
|
@ -52,38 +52,39 @@ namespace Microsoft.AspNet.Mvc
|
|||
}
|
||||
}
|
||||
|
||||
var bodyBoundParameterCount = parameterMetadata.Count(
|
||||
modelMetadata => modelMetadata.BinderMetadata is IFormatterBinderMetadata);
|
||||
if (bodyBoundParameterCount > 1)
|
||||
{
|
||||
throw new InvalidOperationException(Resources.MultipleBodyParametersAreNotAllowed);
|
||||
}
|
||||
|
||||
var actionArguments = new Dictionary<string, object>(StringComparer.Ordinal);
|
||||
foreach (var parameter in parameterMetadata)
|
||||
{
|
||||
await PopulateArgumentAsync(actionBindingContext, actionArguments, parameter);
|
||||
}
|
||||
|
||||
await PopulateArgumentAsync(actionBindingContext, actionArguments, parameterMetadata);
|
||||
return actionArguments;
|
||||
}
|
||||
|
||||
private async Task PopulateArgumentAsync(
|
||||
ActionBindingContext actionBindingContext,
|
||||
IDictionary<string, object> arguments,
|
||||
ModelMetadata modelMetadata)
|
||||
IEnumerable<ModelMetadata> parameterMetadata)
|
||||
{
|
||||
|
||||
var parameterType = modelMetadata.ModelType;
|
||||
var modelBindingContext = GetModelBindingContext(modelMetadata, actionBindingContext);
|
||||
|
||||
if (await actionBindingContext.ModelBinder.BindModelAsync(modelBindingContext))
|
||||
var operationBindingContext = new OperationBindingContext
|
||||
{
|
||||
arguments[modelMetadata.PropertyName] = modelBindingContext.Model;
|
||||
ModelBinder = actionBindingContext.ModelBinder,
|
||||
ValidatorProvider = actionBindingContext.ValidatorProvider,
|
||||
MetadataProvider = actionBindingContext.MetadataProvider,
|
||||
HttpContext = actionBindingContext.ActionContext.HttpContext,
|
||||
ValueProvider = actionBindingContext.ValueProvider,
|
||||
};
|
||||
|
||||
foreach (var parameter in parameterMetadata)
|
||||
{
|
||||
var parameterType = parameter.ModelType;
|
||||
var modelBindingContext = GetModelBindingContext(parameter, actionBindingContext, operationBindingContext);
|
||||
if (await actionBindingContext.ModelBinder.BindModelAsync(modelBindingContext))
|
||||
{
|
||||
arguments[parameter.PropertyName] = modelBindingContext.Model;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static ModelBindingContext GetModelBindingContext(ModelMetadata modelMetadata, ActionBindingContext actionBindingContext)
|
||||
internal static ModelBindingContext GetModelBindingContext(ModelMetadata modelMetadata,
|
||||
ActionBindingContext actionBindingContext,
|
||||
OperationBindingContext operationBindingContext)
|
||||
{
|
||||
Predicate<string> propertyFilter =
|
||||
propertyName => BindAttribute.IsPropertyAllowed(propertyName,
|
||||
|
|
@ -95,14 +96,11 @@ namespace Microsoft.AspNet.Mvc
|
|||
ModelName = modelMetadata.ModelName ?? modelMetadata.PropertyName,
|
||||
ModelMetadata = modelMetadata,
|
||||
ModelState = actionBindingContext.ActionContext.ModelState,
|
||||
ModelBinder = actionBindingContext.ModelBinder,
|
||||
ValidatorProvider = actionBindingContext.ValidatorProvider,
|
||||
MetadataProvider = actionBindingContext.MetadataProvider,
|
||||
HttpContext = actionBindingContext.ActionContext.HttpContext,
|
||||
PropertyFilter = propertyFilter,
|
||||
// Fallback only if there is no explicit model name set.
|
||||
FallbackToEmptyPrefix = modelMetadata.ModelName == null,
|
||||
ValueProvider = actionBindingContext.ValueProvider,
|
||||
OperationBindingContext = operationBindingContext,
|
||||
};
|
||||
|
||||
return modelBindingContext;
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ namespace Microsoft.AspNet.Mvc
|
|||
if (formatter == null)
|
||||
{
|
||||
var unsupportedContentType = Resources.FormatUnsupportedContentType(
|
||||
bindingContext.HttpContext.Request.ContentType);
|
||||
bindingContext.OperationBindingContext.HttpContext.Request.ContentType);
|
||||
bindingContext.ModelState.AddModelError(bindingContext.ModelName, unsupportedContentType);
|
||||
|
||||
// Should always return true so that the model binding process ends here.
|
||||
|
|
@ -51,8 +51,8 @@ namespace Microsoft.AspNet.Mvc
|
|||
|
||||
// Validate the deserialized object
|
||||
var validationContext = new ModelValidationContext(
|
||||
bindingContext.MetadataProvider,
|
||||
bindingContext.ValidatorProvider,
|
||||
bindingContext.OperationBindingContext.MetadataProvider,
|
||||
bindingContext.OperationBindingContext.ValidatorProvider,
|
||||
bindingContext.ModelState,
|
||||
bindingContext.ModelMetadata,
|
||||
containerMetadata: null,
|
||||
|
|
|
|||
|
|
@ -39,6 +39,13 @@ namespace Microsoft.AspNet.Mvc
|
|||
var modelMetadata = metadataProvider.GetMetadataForType(
|
||||
modelAccessor: null,
|
||||
modelType: typeof(TModel));
|
||||
var operationBindingContext = new OperationBindingContext
|
||||
{
|
||||
ModelBinder = modelBinder,
|
||||
ValidatorProvider = validatorProvider,
|
||||
MetadataProvider = metadataProvider,
|
||||
HttpContext = httpContext
|
||||
};
|
||||
|
||||
var modelBindingContext = new ModelBindingContext
|
||||
{
|
||||
|
|
@ -46,12 +53,9 @@ namespace Microsoft.AspNet.Mvc
|
|||
ModelName = prefix,
|
||||
Model = model,
|
||||
ModelState = modelState,
|
||||
ModelBinder = modelBinder,
|
||||
ValueProvider = valueProvider,
|
||||
ValidatorProvider = validatorProvider,
|
||||
MetadataProvider = metadataProvider,
|
||||
FallbackToEmptyPrefix = true,
|
||||
HttpContext = httpContext
|
||||
OperationBindingContext = operationBindingContext,
|
||||
};
|
||||
|
||||
if (await modelBinder.BindModelAsync(modelBindingContext))
|
||||
|
|
|
|||
|
|
@ -10,22 +10,6 @@ namespace Microsoft.AspNet.Mvc.Core
|
|||
private static readonly ResourceManager _resourceManager
|
||||
= new ResourceManager("Microsoft.AspNet.Mvc.Core.Resources", typeof(Resources).GetTypeInfo().Assembly);
|
||||
|
||||
/// <summary>
|
||||
/// More than one parameter is bound to the HTTP request's content.
|
||||
/// </summary>
|
||||
internal static string MultipleBodyParametersAreNotAllowed
|
||||
{
|
||||
get { return GetString("MultipleBodyParametersAreNotAllowed"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// More than one parameter is bound to the HTTP request's content.
|
||||
/// </summary>
|
||||
internal static string FormatMultipleBodyParametersAreNotAllowed()
|
||||
{
|
||||
return GetString("MultipleBodyParametersAreNotAllowed");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The provided anti-forgery token failed a custom data check.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -117,9 +117,6 @@
|
|||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="MultipleBodyParametersAreNotAllowed" xml:space="preserve">
|
||||
<value>More than one parameter is bound to the HTTP request's content.</value>
|
||||
</data>
|
||||
<data name="AntiForgeryToken_AdditionalDataCheckFailed" xml:space="preserve">
|
||||
<value>The provided anti-forgery token failed a custom data check.</value>
|
||||
</data>
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ namespace Microsoft.AspNet.Mvc
|
|||
/// This attribute is used on action parameters to indicate
|
||||
/// they are bound from the body of the incoming request.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)]
|
||||
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
|
||||
public class FromBodyAttribute : Attribute, IFormatterBinderMetadata
|
||||
{
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ namespace Microsoft.AspNet.Mvc
|
|||
/// This attribute is used on action parameters to indicate that
|
||||
/// they will be bound using form data of the incoming request.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)]
|
||||
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
|
||||
public class FromFormAttribute : Attribute, IFormDataValueProviderMetadata
|
||||
{
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ namespace Microsoft.AspNet.Mvc
|
|||
/// This attribute is used on action parameters to indicate that
|
||||
/// they will be bound using query data of the incoming request.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)]
|
||||
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
|
||||
public class FromQueryAttribute : Attribute, IQueryValueProviderMetadata
|
||||
{
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ namespace Microsoft.AspNet.Mvc
|
|||
/// This attribute is used on action parameters to indicate that
|
||||
/// they will be bound using route data of the incoming request.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)]
|
||||
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
|
||||
public class FromRouteAttribute : Attribute, IRouteDataValueProviderMetadata
|
||||
{
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
{
|
||||
if (bindingContext.ModelType == typeof(CancellationToken))
|
||||
{
|
||||
bindingContext.Model = bindingContext.HttpContext.RequestAborted;
|
||||
bindingContext.Model = bindingContext.OperationBindingContext.HttpContext.RequestAborted;
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -47,10 +47,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
var rawValueArray = RawValueToObjectArray(rawValue);
|
||||
foreach (var rawValueElement in rawValueArray)
|
||||
{
|
||||
var innerBindingContext = new ModelBindingContext(bindingContext)
|
||||
var innerModelMetadata =
|
||||
bindingContext.OperationBindingContext.MetadataProvider.GetMetadataForType(null, typeof(TElement));
|
||||
var innerBindingContext = new ModelBindingContext(bindingContext,
|
||||
bindingContext.ModelName,
|
||||
innerModelMetadata)
|
||||
{
|
||||
ModelMetadata = bindingContext.MetadataProvider.GetMetadataForType(null, typeof(TElement)),
|
||||
ModelName = bindingContext.ModelName,
|
||||
ValueProvider = new CompositeValueProvider
|
||||
{
|
||||
// our temporary provider goes at the front of the list
|
||||
|
|
@ -60,7 +62,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
};
|
||||
|
||||
object boundValue = null;
|
||||
if (await bindingContext.ModelBinder.BindModelAsync(innerBindingContext))
|
||||
if (await bindingContext.OperationBindingContext.ModelBinder.BindModelAsync(innerBindingContext))
|
||||
{
|
||||
boundValue = innerBindingContext.Model;
|
||||
bindingContext.ValidationNode.ChildNodes.Add(innerBindingContext.ValidationNode);
|
||||
|
|
@ -99,18 +101,16 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
foreach (var indexName in indexNames)
|
||||
{
|
||||
var fullChildName = ModelBindingHelper.CreateIndexModelName(bindingContext.ModelName, indexName);
|
||||
var childBindingContext = new ModelBindingContext(bindingContext)
|
||||
{
|
||||
ModelMetadata = bindingContext.MetadataProvider.GetMetadataForType(null, typeof(TElement)),
|
||||
ModelName = fullChildName
|
||||
};
|
||||
var childModelMetadata =
|
||||
bindingContext.OperationBindingContext.MetadataProvider.GetMetadataForType(null, typeof(TElement));
|
||||
var childBindingContext = new ModelBindingContext(bindingContext, fullChildName, childModelMetadata);
|
||||
|
||||
var didBind = false;
|
||||
object boundValue = null;
|
||||
|
||||
var modelType = bindingContext.ModelType;
|
||||
|
||||
if (await bindingContext.ModelBinder.BindModelAsync(childBindingContext))
|
||||
if (await bindingContext.OperationBindingContext.ModelBinder.BindModelAsync(childBindingContext))
|
||||
{
|
||||
didBind = true;
|
||||
boundValue = childBindingContext.Model;
|
||||
|
|
|
|||
|
|
@ -19,16 +19,16 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
var dto = (ComplexModelDto)bindingContext.Model;
|
||||
foreach (var propertyMetadata in dto.PropertyMetadata)
|
||||
{
|
||||
var propertyBindingContext = new ModelBindingContext(bindingContext)
|
||||
{
|
||||
ModelMetadata = propertyMetadata,
|
||||
ModelName = ModelBindingHelper.CreatePropertyModelName(bindingContext.ModelName,
|
||||
propertyMetadata.PropertyName)
|
||||
};
|
||||
var propertyModelName = ModelBindingHelper.CreatePropertyModelName(bindingContext.ModelName,
|
||||
propertyMetadata.PropertyName);
|
||||
|
||||
var propertyBindingContext = new ModelBindingContext(bindingContext,
|
||||
propertyModelName,
|
||||
propertyMetadata);
|
||||
|
||||
// bind and propagate the values
|
||||
// If we can't bind, then leave the result missing (don't add a null).
|
||||
if (await bindingContext.ModelBinder.BindModelAsync(propertyBindingContext))
|
||||
if (await bindingContext.OperationBindingContext.ModelBinder.BindModelAsync(propertyBindingContext))
|
||||
{
|
||||
var result = new ComplexModelDtoResult(propertyBindingContext.Model,
|
||||
propertyBindingContext.ValidationNode);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading.Tasks;
|
||||
|
|
@ -79,8 +80,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
bindingContext.ModelName);
|
||||
}
|
||||
|
||||
var validationContext = new ModelValidationContext(bindingContext.MetadataProvider,
|
||||
bindingContext.ValidatorProvider,
|
||||
var validationContext = new ModelValidationContext(bindingContext.OperationBindingContext.MetadataProvider,
|
||||
bindingContext.OperationBindingContext.ValidatorProvider,
|
||||
bindingContext.ModelState,
|
||||
bindingContext.ModelMetadata,
|
||||
containerMetadata: null);
|
||||
|
|
@ -88,6 +89,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
newBindingContext.ValidationNode.Validate(validationContext, parentNode: null);
|
||||
}
|
||||
|
||||
bindingContext.OperationBindingContext.BodyBindingState =
|
||||
newBindingContext.OperationBindingContext.BodyBindingState;
|
||||
bindingContext.Model = newBindingContext.Model;
|
||||
return true;
|
||||
}
|
||||
|
|
@ -128,10 +131,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
ModelName = modelName,
|
||||
ModelState = oldBindingContext.ModelState,
|
||||
ValueProvider = oldBindingContext.ValueProvider,
|
||||
ValidatorProvider = oldBindingContext.ValidatorProvider,
|
||||
MetadataProvider = oldBindingContext.MetadataProvider,
|
||||
ModelBinder = oldBindingContext.ModelBinder,
|
||||
HttpContext = oldBindingContext.HttpContext,
|
||||
OperationBindingContext = oldBindingContext.OperationBindingContext,
|
||||
PropertyFilter = oldBindingContext.PropertyFilter,
|
||||
};
|
||||
|
||||
|
|
@ -141,11 +141,19 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
newBindingContext.ValidationNode = oldBindingContext.ValidationNode;
|
||||
}
|
||||
|
||||
newBindingContext.OperationBindingContext.BodyBindingState = GetBodyBindingState(oldBindingContext);
|
||||
|
||||
// look at the value providers and see if they need to be restricted.
|
||||
var metadata = oldBindingContext.ModelMetadata.BinderMetadata as IValueProviderMetadata;
|
||||
if (metadata != null)
|
||||
{
|
||||
var valueProvider = oldBindingContext.ValueProvider as IMetadataAwareValueProvider;
|
||||
// ValueProvider property might contain a filtered list of value providers.
|
||||
// While deciding to bind a particular property which is annotated with a IValueProviderMetadata,
|
||||
// instead of refiltering an already filtered list, we need to filter value providers from a global list
|
||||
// of all value providers. This is so that every artifact that is explicitly marked using an
|
||||
// IValueProviderMetadata can restrict model binding to only use value providers which support this
|
||||
// IValueProviderMetadata.
|
||||
var valueProvider = oldBindingContext.OperationBindingContext.ValueProvider as IMetadataAwareValueProvider;
|
||||
if (valueProvider != null)
|
||||
{
|
||||
newBindingContext.ValueProvider = valueProvider.Filter(metadata);
|
||||
|
|
@ -154,5 +162,36 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
|
||||
return newBindingContext;
|
||||
}
|
||||
|
||||
private static BodyBindingState GetBodyBindingState(ModelBindingContext oldBindingContext)
|
||||
{
|
||||
var binderMetadata = oldBindingContext.ModelMetadata.BinderMetadata;
|
||||
var newIsFormatterBasedMetadataFound = binderMetadata is IFormatterBinderMetadata;
|
||||
var newIsFormBasedMetadataFound = binderMetadata is IFormDataValueProviderMetadata;
|
||||
var currentModelNeedsToReadBody = newIsFormatterBasedMetadataFound || newIsFormBasedMetadataFound;
|
||||
var oldState = oldBindingContext.OperationBindingContext.BodyBindingState;
|
||||
|
||||
// We need to throw if there are multiple models which can cause body to be read multiple times.
|
||||
// Reading form data multiple times is ok since we cache form data. For the models marked to read using
|
||||
// formatters, multiple reads are not allowed.
|
||||
if (oldState == BodyBindingState.FormatterBased && currentModelNeedsToReadBody ||
|
||||
oldState == BodyBindingState.FormBased && newIsFormatterBasedMetadataFound)
|
||||
{
|
||||
throw new InvalidOperationException(Resources.MultipleBodyParametersOrPropertiesAreNotAllowed);
|
||||
}
|
||||
|
||||
var state = oldBindingContext.OperationBindingContext.BodyBindingState;
|
||||
if (newIsFormatterBasedMetadataFound)
|
||||
{
|
||||
state = BodyBindingState.FormatterBased;
|
||||
}
|
||||
else if (newIsFormBasedMetadataFound && oldState != BodyBindingState.FormatterBased)
|
||||
{
|
||||
// Only update the model binding state if we have not discovered formatter based state already.
|
||||
state = BodyBindingState.FormBased;
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,16 +22,20 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
_activator = activator;
|
||||
}
|
||||
|
||||
public Task<bool> BindModelAsync(ModelBindingContext bindingContext)
|
||||
public async Task<bool> BindModelAsync(ModelBindingContext bindingContext)
|
||||
{
|
||||
var binderType = ResolveBinderType(bindingContext.ModelType);
|
||||
if (binderType != null)
|
||||
{
|
||||
var binder = (IModelBinder)_activator.CreateInstance(_serviceProvider, binderType);
|
||||
return binder.BindModelAsync(bindingContext);
|
||||
await binder.BindModelAsync(bindingContext);
|
||||
|
||||
// Was able to resolve a binder type, hence we should tell the model binding system to return
|
||||
// true so that none of the other model binders participate.
|
||||
return true;
|
||||
}
|
||||
|
||||
return Task.FromResult(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
private static Type ResolveBinderType(Type modelType)
|
||||
|
|
|
|||
|
|
@ -28,14 +28,15 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
internal async Task<BindResult<TModel>> TryBindStrongModel<TModel>(ModelBindingContext parentBindingContext,
|
||||
string propertyName)
|
||||
{
|
||||
var propertyBindingContext = new ModelBindingContext(parentBindingContext)
|
||||
{
|
||||
ModelMetadata = parentBindingContext.MetadataProvider.GetMetadataForType(modelAccessor: null,
|
||||
modelType: typeof(TModel)),
|
||||
ModelName = ModelBindingHelper.CreatePropertyModelName(parentBindingContext.ModelName, propertyName)
|
||||
};
|
||||
var propertyModelMetadata =
|
||||
parentBindingContext.OperationBindingContext.MetadataProvider.GetMetadataForType(modelAccessor: null,
|
||||
modelType: typeof(TModel));
|
||||
var propertyModelName =
|
||||
ModelBindingHelper.CreatePropertyModelName(parentBindingContext.ModelName, propertyName);
|
||||
var propertyBindingContext =
|
||||
new ModelBindingContext(parentBindingContext, propertyModelName, propertyModelMetadata);
|
||||
|
||||
if (await propertyBindingContext.ModelBinder.BindModelAsync(propertyBindingContext))
|
||||
if (await propertyBindingContext.OperationBindingContext.ModelBinder.BindModelAsync(propertyBindingContext))
|
||||
{
|
||||
var untypedModel = propertyBindingContext.Model;
|
||||
var model = ModelBindingHelper.CastOrDefault<TModel>(untypedModel);
|
||||
|
|
|
|||
|
|
@ -16,35 +16,24 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
public virtual async Task<bool> BindModelAsync(ModelBindingContext bindingContext)
|
||||
{
|
||||
ModelBindingHelper.ValidateBindingContext(bindingContext);
|
||||
|
||||
if (!CanBindType(bindingContext.ModelType))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var topLevelObject = bindingContext.ModelMetadata.ContainerType == null;
|
||||
var isThereAnExplicitAlias = bindingContext.ModelMetadata.ModelName != null;
|
||||
var mutableObjectBinderContext = new MutableObjectBinderContext()
|
||||
{
|
||||
ModelBindingContext = bindingContext,
|
||||
PropertyMetadata = GetMetadataForProperties(bindingContext),
|
||||
};
|
||||
|
||||
|
||||
// The first check is necessary because if we fallback to empty prefix, we do not want to depend
|
||||
// on a value provider to provide a value for empty prefix.
|
||||
var containsPrefix = (bindingContext.ModelName == string.Empty && topLevelObject) ||
|
||||
await bindingContext.ValueProvider.ContainsPrefixAsync(bindingContext.ModelName);
|
||||
|
||||
// Always create the model if
|
||||
// 1. It is a top level object and the model name is empty.
|
||||
// 2. There is a value provider which can provide value for the model name.
|
||||
// 3. There is an explicit alias provided by the user and it is a top level object.
|
||||
// The reson we depend on explicit alias is that otherwise we want the FallToEmptyPrefix codepath
|
||||
// to kick in so that empty prefix values could be bound.
|
||||
if (!containsPrefix && !(isThereAnExplicitAlias && topLevelObject))
|
||||
if (!(await CanCreateModel(mutableObjectBinderContext)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
EnsureModel(bindingContext);
|
||||
var propertyMetadatas = GetMetadataForProperties(bindingContext).ToArray();
|
||||
var dto = CreateAndPopulateDto(bindingContext, propertyMetadatas);
|
||||
var dto = await CreateAndPopulateDto(bindingContext, mutableObjectBinderContext.PropertyMetadata);
|
||||
|
||||
// post-processing, e.g. property setters and hooking up validation
|
||||
ProcessDto(bindingContext, dto);
|
||||
|
|
@ -58,6 +47,121 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
return CanUpdatePropertyInternal(propertyMetadata);
|
||||
}
|
||||
|
||||
internal async Task<bool> CanCreateModel(MutableObjectBinderContext context)
|
||||
{
|
||||
var bindingContext = context.ModelBindingContext;
|
||||
var isTopLevelObject = bindingContext.ModelMetadata.ContainerType == null;
|
||||
var isThereAnExplicitAlias = bindingContext.ModelMetadata.ModelName != null;
|
||||
|
||||
// The fact that this has reached here,
|
||||
// it is a complex object which was not directly bound by any previous model binders.
|
||||
// Check if this was supposed to be handled by a non value provider based binder.
|
||||
// if it was then it should be not be bound using mutable object binder.
|
||||
// This check would prevent it from recursing in if a model contains a property of its own type.
|
||||
// We skip this check if it is a top level object because we want to always evaluate
|
||||
// the creation of top level object (this is also required for ModelBinderAttribute to work.)
|
||||
if (!isTopLevelObject &&
|
||||
bindingContext.ModelMetadata.BinderMetadata != null &&
|
||||
!(bindingContext.ModelMetadata.BinderMetadata is IValueProviderMetadata))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create the object if :
|
||||
// 1. It is a top level model with an explicit user supplied prefix.
|
||||
// In this case since it will never fallback to empty prefix, we need to create the model here.
|
||||
if (isTopLevelObject && isThereAnExplicitAlias)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// 2. It is a top level object and there is no model name ( Fallback to empty prefix case ).
|
||||
// This is necessary as we do not want to depend on a value provider to contain an empty prefix.
|
||||
if (isTopLevelObject && bindingContext.ModelName == string.Empty)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// 3. The model name is not prefixed and a value provider can directly provide a value for the model name.
|
||||
// The fact that it is not prefixed means that the containsPrefixAsync call checks for the exact model name
|
||||
// instead of doing a prefix match.
|
||||
if (!bindingContext.ModelName.Contains(".") &&
|
||||
await bindingContext.ValueProvider.ContainsPrefixAsync(bindingContext.ModelName))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// 4. Any of the model properties can be bound using a value provider.
|
||||
if (await CanValueBindAnyModelProperties(context))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private async Task<bool> CanValueBindAnyModelProperties(MutableObjectBinderContext context)
|
||||
{
|
||||
// We need to enumerate the non marked properties and properties marked with IValueProviderMetadata
|
||||
// instead of checking bindingContext.ValueProvider.ContainsPrefixAsync(bindingContext.ModelName)
|
||||
// because there can be a case
|
||||
// where a value provider might be willing to provide a marked property, which might never be bound.
|
||||
// For example if person.Name is marked with FromQuery, and FormValueProvider has a key person.Name, and the
|
||||
// QueryValueProvider does not, we do not want to create Person.
|
||||
var isAnyPropertyEnabledForValueProviderBasedBinding = false;
|
||||
foreach (var propertyMetadata in context.PropertyMetadata)
|
||||
{
|
||||
// This check will skip properties which are marked explicitly using a non value binder.
|
||||
if (propertyMetadata.BinderMetadata == null ||
|
||||
propertyMetadata.BinderMetadata is IValueProviderMetadata)
|
||||
{
|
||||
isAnyPropertyEnabledForValueProviderBasedBinding = true;
|
||||
|
||||
// If any property can return a true value.
|
||||
if (await CanBindValue(context.ModelBindingContext, propertyMetadata))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!isAnyPropertyEnabledForValueProviderBasedBinding)
|
||||
{
|
||||
// Either there are no properties or all the properties are marked as
|
||||
// a non value provider based marker.
|
||||
// This would be the case when the model has all its properties annotated with
|
||||
// a IBinderMetadata. We want to be able to create such a model.
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private async Task<bool> CanBindValue(ModelBindingContext bindingContext, ModelMetadata metadata)
|
||||
{
|
||||
var valueProvider = bindingContext.ValueProvider;
|
||||
var valueProviderMetadata = metadata.BinderMetadata as IValueProviderMetadata;
|
||||
if (valueProviderMetadata != null)
|
||||
{
|
||||
// if there is a binder metadata and since the property can be bound using a value provider.
|
||||
var metadataAwareValueProvider = bindingContext.OperationBindingContext.ValueProvider as IMetadataAwareValueProvider;
|
||||
if (metadataAwareValueProvider != null)
|
||||
{
|
||||
valueProvider = metadataAwareValueProvider.Filter(valueProviderMetadata);
|
||||
}
|
||||
}
|
||||
|
||||
var propertyModelName = ModelBindingHelper.CreatePropertyModelName(bindingContext.ModelName,
|
||||
metadata.PropertyName);
|
||||
|
||||
if (await valueProvider.ContainsPrefixAsync(propertyModelName))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool CanBindType(Type modelType)
|
||||
{
|
||||
// Simple types cannot use this binder
|
||||
|
|
@ -106,19 +210,18 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
return true;
|
||||
}
|
||||
|
||||
private ComplexModelDto CreateAndPopulateDto(ModelBindingContext bindingContext,
|
||||
private async Task<ComplexModelDto> CreateAndPopulateDto(ModelBindingContext bindingContext,
|
||||
IEnumerable<ModelMetadata> propertyMetadatas)
|
||||
{
|
||||
// create a DTO and call into the DTO binder
|
||||
var originalDto = new ComplexModelDto(bindingContext.ModelMetadata, propertyMetadatas);
|
||||
var dtoBindingContext = new ModelBindingContext(bindingContext)
|
||||
{
|
||||
ModelMetadata = bindingContext.MetadataProvider.GetMetadataForType(() => originalDto,
|
||||
typeof(ComplexModelDto)),
|
||||
ModelName = bindingContext.ModelName
|
||||
};
|
||||
var complexModelDtoMetadata =
|
||||
bindingContext.OperationBindingContext.MetadataProvider.GetMetadataForType(() => originalDto,
|
||||
typeof(ComplexModelDto));
|
||||
var dtoBindingContext =
|
||||
new ModelBindingContext(bindingContext, bindingContext.ModelName, complexModelDtoMetadata);
|
||||
|
||||
bindingContext.ModelBinder.BindModelAsync(dtoBindingContext);
|
||||
await bindingContext.OperationBindingContext.ModelBinder.BindModelAsync(dtoBindingContext);
|
||||
return (ComplexModelDto)dtoBindingContext.Model;
|
||||
}
|
||||
|
||||
|
|
@ -165,8 +268,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
protected virtual IEnumerable<ModelMetadata> GetMetadataForProperties(ModelBindingContext bindingContext)
|
||||
{
|
||||
var validationInfo = GetPropertyValidationInfo(bindingContext);
|
||||
var propertyTypeMetadata = bindingContext.MetadataProvider
|
||||
.GetMetadataForType(null, bindingContext.ModelType);
|
||||
var propertyTypeMetadata = bindingContext.OperationBindingContext
|
||||
.MetadataProvider
|
||||
.GetMetadataForType(null, bindingContext.ModelType);
|
||||
Predicate<string> newPropertyFilter =
|
||||
propertyName => bindingContext.PropertyFilter(propertyName) &&
|
||||
BindAttribute.IsPropertyAllowed(
|
||||
|
|
@ -198,7 +302,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
{
|
||||
var propertyName = property.Name;
|
||||
var propertyMetadata = bindingContext.PropertyMetadata[propertyName];
|
||||
var requiredValidator = bindingContext.ValidatorProvider
|
||||
var requiredValidator = bindingContext.OperationBindingContext
|
||||
.ValidatorProvider
|
||||
.GetValidators(propertyMetadata)
|
||||
.FirstOrDefault(v => v != null && v.IsRequired);
|
||||
if (requiredValidator != null)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
{
|
||||
public class MutableObjectBinderContext
|
||||
{
|
||||
public ModelBindingContext ModelBindingContext { get; set; }
|
||||
|
||||
public IEnumerable<ModelMetadata> PropertyMetadata { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents state of models which are bound using body.
|
||||
/// </summary>
|
||||
public enum BodyBindingState
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents if there has been no metadata found which needs to read the body during the current
|
||||
/// model binding process.
|
||||
/// </summary>
|
||||
NotBodyBased,
|
||||
|
||||
/// <summary>
|
||||
/// Represents if there is a <see cref="IFormatterBinderMetadata"/> that
|
||||
/// has been found during the current model binding process.
|
||||
/// </summary>
|
||||
FormatterBased,
|
||||
|
||||
/// <summary>
|
||||
/// Represents if there is a <see cref = "IFormDataValueProviderMetadata" /> that
|
||||
/// has been found during the current model binding process.
|
||||
/// </summary>
|
||||
FormBased
|
||||
}
|
||||
}
|
||||
|
|
@ -83,7 +83,17 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
parameterName,
|
||||
binderMetadata);
|
||||
|
||||
return CreateMetadataFromPrototype(parameterInfo.Prototype, modelAccessor);
|
||||
var metadata = CreateMetadataFromPrototype(parameterInfo.Prototype, modelAccessor);
|
||||
|
||||
|
||||
// If there is no metadata associated with the parameter itself get it from the type.
|
||||
if (metadata != null && metadata.BinderMetadata == null)
|
||||
{
|
||||
var typeInfo = GetTypeInformation(parameter.ParameterType);
|
||||
metadata.BinderMetadata = typeInfo.Prototype.BinderMetadata;
|
||||
}
|
||||
|
||||
return metadata;
|
||||
}
|
||||
|
||||
private IEnumerable<ModelMetadata> GetMetadataForPropertiesCore(object container, Type containerType)
|
||||
|
|
@ -115,6 +125,18 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
metadata.IsReadOnly = true;
|
||||
}
|
||||
|
||||
// We need to update the property after the prototype creation because otherwise
|
||||
// if the property type is same as the containing type, it would cause infinite recursion.
|
||||
// If there is no metadata associated with the property itself get it from the type.
|
||||
if (metadata != null && metadata.BinderMetadata == null)
|
||||
{
|
||||
if (propertyInfo.Prototype != null)
|
||||
{
|
||||
var typeInfo = GetTypeInformation(propertyInfo.Prototype.ModelType);
|
||||
metadata.BinderMetadata = typeInfo.Prototype.BinderMetadata;
|
||||
}
|
||||
}
|
||||
|
||||
return metadata;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,25 +29,32 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ModelBindingContext"/> class using the
|
||||
/// <param name="bindingContext" />.
|
||||
// </summary>
|
||||
/// <paramref name="bindingContext" />.
|
||||
/// </summary>
|
||||
/// <param name="bindingContext">Existing <see cref="ModelBindingContext"/>.</param>
|
||||
/// <param name="modelName">Model name of associated with the new <see cref="ModelBindingContext"/>.</param>
|
||||
/// <param name="modelMetadata">Model metadata of associated with the new <see cref="ModelBindingContext"/>.
|
||||
/// </param>
|
||||
/// <remarks>
|
||||
/// This constructor copies certain values that won't change between parent and child objects,
|
||||
/// e.g. ValueProvider, ModelState
|
||||
/// </remarks>
|
||||
public ModelBindingContext(ModelBindingContext bindingContext)
|
||||
public ModelBindingContext([NotNull] ModelBindingContext bindingContext,
|
||||
[NotNull] string modelName,
|
||||
[NotNull] ModelMetadata modelMetadata)
|
||||
{
|
||||
if (bindingContext != null)
|
||||
{
|
||||
ModelState = bindingContext.ModelState;
|
||||
ValueProvider = bindingContext.ValueProvider;
|
||||
MetadataProvider = bindingContext.MetadataProvider;
|
||||
ModelBinder = bindingContext.ModelBinder;
|
||||
ValidatorProvider = bindingContext.ValidatorProvider;
|
||||
HttpContext = bindingContext.HttpContext;
|
||||
}
|
||||
ModelName = modelName;
|
||||
ModelMetadata = modelMetadata;
|
||||
ModelState = bindingContext.ModelState;
|
||||
ValueProvider = bindingContext.ValueProvider;
|
||||
OperationBindingContext = bindingContext.OperationBindingContext;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents the <see cref="OperationBindingContext"/> associated with this context.
|
||||
/// </summary>
|
||||
public OperationBindingContext OperationBindingContext { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the model associated with this context.
|
||||
/// </summary>
|
||||
|
|
@ -129,32 +136,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
/// </summary>
|
||||
public bool FallbackToEmptyPrefix { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="HttpContext"/> for the current request.
|
||||
/// </summary>
|
||||
public HttpContext HttpContext { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="IValueProvider"/> associated with this context.
|
||||
/// </summary>
|
||||
public IValueProvider ValueProvider { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="IModelBinder"/> associated with this context.
|
||||
/// </summary>
|
||||
public IModelBinder ModelBinder { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="IModelMetadataProvider"/> associated with this context.
|
||||
/// </summary>
|
||||
public IModelMetadataProvider MetadataProvider { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="IModelValidatorProvider"/> instance used for model validation with this
|
||||
/// context.
|
||||
/// </summary>
|
||||
public IModelValidatorProvider ValidatorProvider { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a dictionary of property name to <see cref="ModelMetadata"/> instances for
|
||||
/// <see cref="ModelMetadata.Properties"/>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,46 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNet.Http;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
{
|
||||
/// <summary>
|
||||
/// A context that contains information specific to the current request and the action whose parameters
|
||||
/// are being model bound.
|
||||
/// </summary>
|
||||
public class OperationBindingContext
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents if there has been a body bound model found during the current model binding process.
|
||||
/// </summary>
|
||||
public BodyBindingState BodyBindingState { get; set; } = BodyBindingState.NotBodyBased;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="HttpContext"/> for the current request.
|
||||
/// </summary>
|
||||
public HttpContext HttpContext { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets unaltered value provider collection.
|
||||
/// Value providers can be filtered by specific model binders.
|
||||
/// </summary>
|
||||
public IValueProvider ValueProvider { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="IModelBinder"/> associated with this context.
|
||||
/// </summary>
|
||||
public IModelBinder ModelBinder { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="IModelMetadataProvider"/> associated with this context.
|
||||
/// </summary>
|
||||
public IModelMetadataProvider MetadataProvider { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="IModelValidatorProvider"/> instance used for model validation with this
|
||||
/// context.
|
||||
/// </summary>
|
||||
public IModelValidatorProvider ValidatorProvider { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -10,6 +10,22 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
private static readonly ResourceManager _resourceManager
|
||||
= new ResourceManager("Microsoft.AspNet.Mvc.ModelBinding.Resources", typeof(Resources).GetTypeInfo().Assembly);
|
||||
|
||||
/// <summary>
|
||||
/// More than one parameter and/or property is bound to the HTTP request's content.
|
||||
/// </summary>
|
||||
internal static string MultipleBodyParametersOrPropertiesAreNotAllowed
|
||||
{
|
||||
get { return GetString("MultipleBodyParametersOrPropertiesAreNotAllowed"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// More than one parameter and/or property is bound to the HTTP request's content.
|
||||
/// </summary>
|
||||
internal static string FormatMultipleBodyParametersOrPropertiesAreNotAllowed()
|
||||
{
|
||||
return GetString("MultipleBodyParametersOrPropertiesAreNotAllowed");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Value cannot be null or empty.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -117,6 +117,9 @@
|
|||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="MultipleBodyParametersOrPropertiesAreNotAllowed" xml:space="preserve">
|
||||
<value>More than one parameter and/or property is bound to the HTTP request's content.</value>
|
||||
</data>
|
||||
<data name="ArgumentCannotBeNullOrEmpty" xml:space="preserve">
|
||||
<value>Value cannot be null or empty.</value>
|
||||
</data>
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
{
|
||||
public ModelValidationContext([NotNull] ModelBindingContext bindingContext,
|
||||
[NotNull] ModelMetadata metadata)
|
||||
: this(bindingContext.MetadataProvider,
|
||||
bindingContext.ValidatorProvider,
|
||||
: this(bindingContext.OperationBindingContext.MetadataProvider,
|
||||
bindingContext.OperationBindingContext.ValidatorProvider,
|
||||
bindingContext.ModelState,
|
||||
metadata,
|
||||
bindingContext.ModelMetadata)
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ namespace Microsoft.AspNet.Mvc.WebApiCompatShim
|
|||
{
|
||||
if (bindingContext.ModelType == typeof(HttpRequestMessage))
|
||||
{
|
||||
bindingContext.Model = bindingContext.HttpContext.GetHttpRequestMessage();
|
||||
bindingContext.Model = bindingContext.OperationBindingContext.HttpContext.GetHttpRequestMessage();
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ namespace Microsoft.AspNet.Mvc
|
|||
// Arrange
|
||||
var bindingContext = GetBindingContext(typeof(Person), inputFormatter: null);
|
||||
bindingContext.ModelMetadata.BinderMetadata = Mock.Of<IFormatterBinderMetadata>();
|
||||
var binder = bindingContext.ModelBinder;
|
||||
var binder = bindingContext.OperationBindingContext.ModelBinder;
|
||||
|
||||
// Act
|
||||
var binderResult = await binder.BindModelAsync(bindingContext);
|
||||
|
|
@ -70,7 +70,7 @@ namespace Microsoft.AspNet.Mvc
|
|||
var bindingContext = GetBindingContext(typeof(Person), inputFormatter: null);
|
||||
bindingContext.ModelMetadata.BinderMetadata = useBody ? Mock.Of<IFormatterBinderMetadata>() :
|
||||
Mock.Of<IBinderMetadata>();
|
||||
var binder = bindingContext.ModelBinder;
|
||||
var binder = bindingContext.OperationBindingContext.ModelBinder;
|
||||
|
||||
// Act
|
||||
var binderResult = await binder.BindModelAsync(bindingContext);
|
||||
|
|
@ -82,15 +82,20 @@ namespace Microsoft.AspNet.Mvc
|
|||
private static ModelBindingContext GetBindingContext(Type modelType, IInputFormatter inputFormatter)
|
||||
{
|
||||
var metadataProvider = new EmptyModelMetadataProvider();
|
||||
var operationBindingContext = new OperationBindingContext
|
||||
{
|
||||
ModelBinder = GetBodyBinder(inputFormatter, null),
|
||||
MetadataProvider = metadataProvider,
|
||||
HttpContext = new DefaultHttpContext(),
|
||||
};
|
||||
|
||||
ModelBindingContext bindingContext = new ModelBindingContext
|
||||
{
|
||||
ModelMetadata = metadataProvider.GetMetadataForType(null, modelType),
|
||||
ModelName = "someName",
|
||||
ValueProvider = Mock.Of<IValueProvider>(),
|
||||
ModelBinder = GetBodyBinder(inputFormatter, null),
|
||||
MetadataProvider = metadataProvider,
|
||||
HttpContext = new DefaultHttpContext(),
|
||||
ModelState = new ModelStateDictionary()
|
||||
ModelState = new ModelStateDictionary(),
|
||||
OperationBindingContext = operationBindingContext,
|
||||
};
|
||||
|
||||
return bindingContext;
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ namespace Microsoft.AspNet.Mvc.Core.Test
|
|||
Mock.Of<IModelValidatorProvider>());
|
||||
// Act
|
||||
var context = DefaultControllerActionArgumentBinder
|
||||
.GetModelBindingContext(modelMetadata, actionBindingContext);
|
||||
.GetModelBindingContext(modelMetadata, actionBindingContext, Mock.Of<OperationBindingContext>());
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedFallToEmptyPrefix, context.FallbackToEmptyPrefix);
|
||||
|
|
@ -106,63 +106,13 @@ namespace Microsoft.AspNet.Mvc.Core.Test
|
|||
Mock.Of<IModelValidatorProvider>());
|
||||
// Act
|
||||
var context = DefaultControllerActionArgumentBinder
|
||||
.GetModelBindingContext(modelMetadata, actionBindingContext);
|
||||
.GetModelBindingContext(modelMetadata, actionBindingContext, Mock.Of<OperationBindingContext>());
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedFallToEmptyPrefix, context.FallbackToEmptyPrefix);
|
||||
Assert.Equal(expectedModelName, context.ModelName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Parameters_WithMultipleFromBody_Throw()
|
||||
{
|
||||
// Arrange
|
||||
var actionDescriptor = new ControllerActionDescriptor
|
||||
{
|
||||
MethodInfo = typeof(TestController).GetMethod("ActionWithTwoBodyParam"),
|
||||
Parameters = new List<ParameterDescriptor>
|
||||
{
|
||||
new ParameterDescriptor
|
||||
{
|
||||
Name = "bodyParam",
|
||||
ParameterType = typeof(Person),
|
||||
},
|
||||
new ParameterDescriptor
|
||||
{
|
||||
Name = "bodyParam1",
|
||||
ParameterType = typeof(Person),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var binder = new Mock<IModelBinder>();
|
||||
var metadataProvider = new DataAnnotationsModelMetadataProvider();
|
||||
var actionContext = new ActionContext(new RouteContext(Mock.Of<HttpContext>()),
|
||||
actionDescriptor);
|
||||
actionContext.Controller = Mock.Of<object>();
|
||||
var bindingContext = new ActionBindingContext(actionContext,
|
||||
metadataProvider,
|
||||
Mock.Of<IModelBinder>(),
|
||||
Mock.Of<IValueProvider>(),
|
||||
Mock.Of<IInputFormatterSelector>(),
|
||||
Mock.Of<IModelValidatorProvider>());
|
||||
|
||||
var actionBindingContextProvider = new Mock<IActionBindingContextProvider>();
|
||||
actionBindingContextProvider.Setup(p => p.GetActionBindingContextAsync(It.IsAny<ActionContext>()))
|
||||
.Returns(Task.FromResult(bindingContext));
|
||||
|
||||
var invoker = new DefaultControllerActionArgumentBinder(
|
||||
actionBindingContextProvider.Object);
|
||||
|
||||
// Act
|
||||
var ex = await Assert.ThrowsAsync<InvalidOperationException>(
|
||||
() => invoker.GetActionArgumentsAsync(actionContext));
|
||||
|
||||
// Assert
|
||||
Assert.Equal("More than one parameter is bound to the HTTP request's content.",
|
||||
ex.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetActionArgumentsAsync_DoesNotAddActionArguments_IfBinderReturnsFalse()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ using System.Collections.Generic;
|
|||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Builder;
|
||||
using Microsoft.AspNet.TestHost;
|
||||
|
|
@ -48,6 +49,32 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
|||
Assert.Equal(expectedValue, await response.Content.ReadAsStringAsync());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task TryUpdateModel_WithAPropertyFromBody()
|
||||
{
|
||||
// Arrange
|
||||
var server = TestServer.Create(_services, _app);
|
||||
var client = server.CreateClient();
|
||||
|
||||
// the name would be of customer.Department.Name
|
||||
// and not for the top level customer object.
|
||||
var input = "{\"Name\":\"RandomDepartment\"}";
|
||||
var content = new StringContent(input, Encoding.UTF8, "application/json");
|
||||
|
||||
// Act
|
||||
var response = await client.PostAsync("http://localhost/Home/GetCustomer?Id=1234", content);
|
||||
|
||||
//Assert
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
var customer = JsonConvert.DeserializeObject<Customer>(
|
||||
await response.Content.ReadAsStringAsync());
|
||||
Assert.NotNull(customer.Department);
|
||||
Assert.Equal("RandomDepartment", customer.Department.Name);
|
||||
Assert.Equal(1234, customer.Id);
|
||||
Assert.Equal(25, customer.Age);
|
||||
Assert.Equal("dummy", customer.Name);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task MultipleParametersMarkedWithFromBody_Throws()
|
||||
{
|
||||
|
|
@ -57,12 +84,280 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
|||
|
||||
// Act & Assert
|
||||
var ex = await Assert.ThrowsAsync<InvalidOperationException>(() =>
|
||||
client.GetAsync("http://localhost/MultipleParametersFromBody/MultipleParametersFromBodyThrows"));
|
||||
client.GetAsync("http://localhost/FromAttributes/FromBodyParametersThrows"));
|
||||
|
||||
Assert.Equal("More than one parameter is bound to the HTTP request's content.",
|
||||
Assert.Equal("More than one parameter and/or property is bound to the HTTP request's content.",
|
||||
ex.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task MultipleParameterAndPropertiesMarkedWithFromBody_Throws()
|
||||
{
|
||||
// Arrange
|
||||
var server = TestServer.Create(_services, _app);
|
||||
var client = server.CreateClient();
|
||||
|
||||
// Act & Assert
|
||||
var ex = await Assert.ThrowsAsync<InvalidOperationException>(() =>
|
||||
client.GetAsync("http://localhost/FromAttributes/FromBodyParameterAndPropertyThrows"));
|
||||
|
||||
Assert.Equal("More than one parameter and/or property is bound to the HTTP request's content.",
|
||||
ex.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task MultipleParametersMarkedWith_FromFormAndFromBody_Throws()
|
||||
{
|
||||
// Arrange
|
||||
var server = TestServer.Create(_services, _app);
|
||||
var client = server.CreateClient();
|
||||
|
||||
// Act & Assert
|
||||
var ex = await Assert.ThrowsAsync<InvalidOperationException>(() =>
|
||||
client.GetAsync("http://localhost/FromAttributes/FormAndBody_AsParameters_Throws"));
|
||||
|
||||
Assert.Equal("More than one parameter and/or property is bound to the HTTP request's content.",
|
||||
ex.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task MultipleParameterAndPropertiesMarkedWith_FromFormAndFromBody_Throws()
|
||||
{
|
||||
// Arrange
|
||||
var server = TestServer.Create(_services, _app);
|
||||
var client = server.CreateClient();
|
||||
|
||||
// Act & Assert
|
||||
var ex = await Assert.ThrowsAsync<InvalidOperationException>(() =>
|
||||
client.GetAsync("http://localhost/FromAttributes/FormAndBody_Throws"));
|
||||
|
||||
Assert.Equal("More than one parameter and/or property is bound to the HTTP request's content.",
|
||||
ex.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CanBind_MultipleParameters_UsingFromForm()
|
||||
{
|
||||
// Arrange
|
||||
var server = TestServer.Create(_services, _app);
|
||||
var client = server.CreateClient();
|
||||
|
||||
var request = new HttpRequestMessage(HttpMethod.Post,
|
||||
"http://localhost/FromAttributes/MultipleFromFormParameters");
|
||||
var nameValueCollection = new List<KeyValuePair<string, string>>
|
||||
{
|
||||
new KeyValuePair<string,string>("homeAddress.Street", "1"),
|
||||
new KeyValuePair<string,string>("homeAddress.State", "WA_Form_Home"),
|
||||
new KeyValuePair<string,string>("homeAddress.Zip", "2"),
|
||||
new KeyValuePair<string,string>("officeAddress.Street", "3"),
|
||||
new KeyValuePair<string,string>("officeAddress.State", "WA_Form_Office"),
|
||||
new KeyValuePair<string,string>("officeAddress.Zip", "4"),
|
||||
};
|
||||
|
||||
request.Content = new FormUrlEncodedContent(nameValueCollection);
|
||||
|
||||
// Act
|
||||
var response = await client.SendAsync(request);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
var user = JsonConvert.DeserializeObject<User_FromForm>(
|
||||
await response.Content.ReadAsStringAsync());
|
||||
|
||||
Assert.Equal("WA_Form_Home", user.HomeAddress.State);
|
||||
Assert.Equal(1, user.HomeAddress.Street);
|
||||
Assert.Equal(2, user.HomeAddress.Zip);
|
||||
|
||||
Assert.Equal("WA_Form_Office", user.OfficeAddress.State);
|
||||
Assert.Equal(3, user.OfficeAddress.Street);
|
||||
Assert.Equal(4, user.OfficeAddress.Zip);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CanBind_MultipleProperties_UsingFromForm()
|
||||
{
|
||||
// Arrange
|
||||
var server = TestServer.Create(_services, _app);
|
||||
var client = server.CreateClient();
|
||||
|
||||
var request = new HttpRequestMessage(HttpMethod.Post,
|
||||
"http://localhost/FromAttributes/MultipleFromFormParameterAndProperty");
|
||||
var nameValueCollection = new List<KeyValuePair<string, string>>
|
||||
{
|
||||
new KeyValuePair<string,string>("Street", "1"),
|
||||
new KeyValuePair<string,string>("State", "WA_Form_Home"),
|
||||
new KeyValuePair<string,string>("Zip", "2"),
|
||||
new KeyValuePair<string,string>("officeAddress.Street", "3"),
|
||||
new KeyValuePair<string,string>("officeAddress.State", "WA_Form_Office"),
|
||||
new KeyValuePair<string,string>("officeAddress.Zip", "4"),
|
||||
};
|
||||
|
||||
request.Content = new FormUrlEncodedContent(nameValueCollection);
|
||||
|
||||
// Act
|
||||
var response = await client.SendAsync(request);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
var user = JsonConvert.DeserializeObject<User_FromForm>(
|
||||
await response.Content.ReadAsStringAsync());
|
||||
|
||||
Assert.Equal("WA_Form_Home", user.HomeAddress.State);
|
||||
Assert.Equal(1, user.HomeAddress.Street);
|
||||
Assert.Equal(2, user.HomeAddress.Zip);
|
||||
|
||||
Assert.Equal("WA_Form_Office", user.OfficeAddress.State);
|
||||
Assert.Equal(3, user.OfficeAddress.Street);
|
||||
Assert.Equal(4, user.OfficeAddress.Zip);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CanBind_ComplexData_OnParameters_UsingFromAttributes()
|
||||
{
|
||||
// Arrange
|
||||
var server = TestServer.Create(_services, _app);
|
||||
var client = server.CreateClient();
|
||||
|
||||
// Provide all three values, it should bind based on the attribute on the action method.
|
||||
var request = new HttpRequestMessage(HttpMethod.Post,
|
||||
"http://localhost/FromAttributes/GetUser/5/WA_Route/6" +
|
||||
"?Street=3&State=WA_Query&Zip=4");
|
||||
var nameValueCollection = new List<KeyValuePair<string, string>>
|
||||
{
|
||||
new KeyValuePair<string,string>("Street", "1"),
|
||||
new KeyValuePair<string,string>("State", "WA_Form"),
|
||||
new KeyValuePair<string,string>("Zip", "2"),
|
||||
};
|
||||
|
||||
request.Content = new FormUrlEncodedContent(nameValueCollection);
|
||||
|
||||
// Act
|
||||
var response = await client.SendAsync(request);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
var user = JsonConvert.DeserializeObject<User_FromForm>(
|
||||
await response.Content.ReadAsStringAsync());
|
||||
|
||||
// Assert FromRoute
|
||||
Assert.Equal("WA_Route", user.HomeAddress.State);
|
||||
Assert.Equal(5, user.HomeAddress.Street);
|
||||
Assert.Equal(6, user.HomeAddress.Zip);
|
||||
|
||||
// Assert FromForm
|
||||
Assert.Equal("WA_Form", user.OfficeAddress.State);
|
||||
Assert.Equal(1, user.OfficeAddress.Street);
|
||||
Assert.Equal(2, user.OfficeAddress.Zip);
|
||||
|
||||
// Assert FromQuery
|
||||
Assert.Equal("WA_Query", user.ShippingAddress.State);
|
||||
Assert.Equal(3, user.ShippingAddress.Street);
|
||||
Assert.Equal(4, user.ShippingAddress.Zip);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CanBind_ComplexData_OnProperties_UsingFromAttributes()
|
||||
{
|
||||
// Arrange
|
||||
var server = TestServer.Create(_services, _app);
|
||||
var client = server.CreateClient();
|
||||
|
||||
// Provide all three values, it should bind based on the attribute on the action method.
|
||||
var request = new HttpRequestMessage(HttpMethod.Post,
|
||||
"http://localhost/FromAttributes/GetUser_FromForm/5/WA_Route/6" +
|
||||
"?ShippingAddress.Street=3&ShippingAddress.State=WA_Query&ShippingAddress.Zip=4");
|
||||
var nameValueCollection = new List<KeyValuePair<string, string>>
|
||||
{
|
||||
new KeyValuePair<string,string>("OfficeAddress.Street", "1"),
|
||||
new KeyValuePair<string,string>("OfficeAddress.State", "WA_Form"),
|
||||
new KeyValuePair<string,string>("OfficeAddress.Zip", "2"),
|
||||
};
|
||||
|
||||
request.Content = new FormUrlEncodedContent(nameValueCollection);
|
||||
|
||||
// Act
|
||||
var response = await client.SendAsync(request);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
var user = JsonConvert.DeserializeObject<User_FromForm>(
|
||||
await response.Content.ReadAsStringAsync());
|
||||
|
||||
// Assert FromRoute
|
||||
Assert.Equal("WA_Route", user.HomeAddress.State);
|
||||
Assert.Equal(5, user.HomeAddress.Street);
|
||||
Assert.Equal(6, user.HomeAddress.Zip);
|
||||
|
||||
// Assert FromForm
|
||||
Assert.Equal("WA_Form", user.OfficeAddress.State);
|
||||
Assert.Equal(1, user.OfficeAddress.Street);
|
||||
Assert.Equal(2, user.OfficeAddress.Zip);
|
||||
|
||||
// Assert FromQuery
|
||||
Assert.Equal("WA_Query", user.ShippingAddress.State);
|
||||
Assert.Equal(3, user.ShippingAddress.Street);
|
||||
Assert.Equal(4, user.ShippingAddress.Zip);
|
||||
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CanBind_ComplexData_OnProperties_UsingFromAttributes_WithBody()
|
||||
{
|
||||
// Arrange
|
||||
var server = TestServer.Create(_services, _app);
|
||||
var client = server.CreateClient();
|
||||
|
||||
// Provide all three values, it should bind based on the attribute on the action method.
|
||||
var request = new HttpRequestMessage(HttpMethod.Post,
|
||||
"http://localhost/FromAttributes/GetUser_FromBody/5/WA_Route/6" +
|
||||
"?ShippingAddress.Street=3&ShippingAddress.State=WA_Query&ShippingAddress.Zip=4");
|
||||
var input = "{\"State\":\"WA_Body\",\"Street\":1,\"Zip\":2}";
|
||||
|
||||
request.Content = new StringContent(input, Encoding.UTF8, "application/json");
|
||||
|
||||
// Act
|
||||
var response = await client.SendAsync(request);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
var user = JsonConvert.DeserializeObject<User_FromBody>(
|
||||
await response.Content.ReadAsStringAsync());
|
||||
|
||||
// Assert FromRoute
|
||||
Assert.Equal("WA_Route", user.HomeAddress.State);
|
||||
Assert.Equal(5, user.HomeAddress.Street);
|
||||
Assert.Equal(6, user.HomeAddress.Zip);
|
||||
|
||||
// Assert FromBody
|
||||
Assert.Equal("WA_Body", user.OfficeAddress.State);
|
||||
Assert.Equal(1, user.OfficeAddress.Street);
|
||||
Assert.Equal(2, user.OfficeAddress.Zip);
|
||||
|
||||
// Assert FromQuery
|
||||
Assert.Equal("WA_Query", user.ShippingAddress.State);
|
||||
Assert.Equal(3, user.ShippingAddress.Street);
|
||||
Assert.Equal(4, user.ShippingAddress.Zip);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public async Task NonExistingModelBinder_ForABinderMetadata_DoesNotRecurseInfinitely()
|
||||
{
|
||||
// Arrange
|
||||
var server = TestServer.Create(_services, _app);
|
||||
var client = server.CreateClient();
|
||||
|
||||
// Act & Assert
|
||||
var response = await client.GetStringAsync("http://localhost/WithMetadata/EchoDocument");
|
||||
|
||||
var document = JsonConvert.DeserializeObject<Document>
|
||||
(response);
|
||||
|
||||
Assert.NotNull(document);
|
||||
Assert.Null(document.Version);
|
||||
Assert.Null(document.SubDocument);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ParametersWithNoValueProviderMetadataUseTheAvailableValueProviders()
|
||||
{
|
||||
|
|
@ -86,7 +381,7 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ParametersAreAlwaysCreated()
|
||||
public async Task ParametersAreAlwaysCreated_IfValuesAreProvidedWithoutModelName()
|
||||
{
|
||||
// Arrange
|
||||
var server = TestServer.Create(_services, _app);
|
||||
|
|
@ -107,6 +402,136 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
|||
Assert.Equal(12, person.Age);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ParametersAreAlwaysCreated_IfValueIsProvidedForModelName()
|
||||
{
|
||||
// Arrange
|
||||
var server = TestServer.Create(_services, _app);
|
||||
var client = server.CreateClient();
|
||||
|
||||
// Act
|
||||
var response = await
|
||||
client.GetAsync("http://localhost/WithoutMetadata" +
|
||||
"/GetPersonParameter?p="); // here p is the model name.
|
||||
|
||||
//Assert
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
var person = JsonConvert.DeserializeObject<Person>(
|
||||
await response.Content.ReadAsStringAsync());
|
||||
Assert.NotNull(person);
|
||||
Assert.Null(person.Name);
|
||||
Assert.Equal(0, person.Age);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ParametersAreAlwaysCreated_IfNoValuesAreProvided()
|
||||
{
|
||||
// Arrange
|
||||
var server = TestServer.Create(_services, _app);
|
||||
var client = server.CreateClient();
|
||||
|
||||
// Act
|
||||
var response = await
|
||||
client.GetAsync("http://localhost/WithoutMetadata" +
|
||||
"/GetPersonParameter");
|
||||
|
||||
//Assert
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
var person = JsonConvert.DeserializeObject<Person>(
|
||||
await response.Content.ReadAsStringAsync());
|
||||
Assert.NotNull(person);
|
||||
Assert.Null(person.Name);
|
||||
Assert.Equal(0, person.Age);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PropertiesAreBound_IfTheyAreProvidedByValueProviders()
|
||||
{
|
||||
// Arrange
|
||||
var server = TestServer.Create(_services, _app);
|
||||
var client = server.CreateClient();
|
||||
|
||||
// Act
|
||||
var response = await
|
||||
client.GetAsync("http://localhost/Properties" +
|
||||
"/GetCompany?Employees[0].Name=somename&Age=12");
|
||||
|
||||
//Assert
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
var company = JsonConvert.DeserializeObject<Company>(
|
||||
await response.Content.ReadAsStringAsync());
|
||||
Assert.NotNull(company);
|
||||
Assert.NotNull(company.Employees);
|
||||
Assert.Equal(1, company.Employees.Count);
|
||||
Assert.NotNull(company.Employees[0].Name);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PropertiesAreBound_IfTheyAreMarkedExplicitly()
|
||||
{
|
||||
// Arrange
|
||||
var server = TestServer.Create(_services, _app);
|
||||
var client = server.CreateClient();
|
||||
|
||||
// Act
|
||||
var response = await
|
||||
client.GetAsync("http://localhost/Properties" +
|
||||
"/GetCompany");
|
||||
|
||||
//Assert
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
var company = JsonConvert.DeserializeObject<Company>(
|
||||
await response.Content.ReadAsStringAsync());
|
||||
Assert.NotNull(company);
|
||||
Assert.NotNull(company.CEO);
|
||||
Assert.Null(company.CEO.Name);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PropertiesAreBound_IfTheyArePocoMetadataMarkedTypes()
|
||||
{
|
||||
// Arrange
|
||||
var server = TestServer.Create(_services, _app);
|
||||
var client = server.CreateClient();
|
||||
|
||||
// Act
|
||||
var response = await
|
||||
client.GetAsync("http://localhost/Properties" +
|
||||
"/GetCompany");
|
||||
|
||||
//Assert
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
var company = JsonConvert.DeserializeObject<Company>(
|
||||
await response.Content.ReadAsStringAsync());
|
||||
Assert.NotNull(company);
|
||||
|
||||
// Department property is not null because it was a marker poco.
|
||||
Assert.NotNull(company.Department);
|
||||
|
||||
// beacause no value is provided.
|
||||
Assert.Null(company.Department.Name);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PropertiesAreNotBound_ByDefault()
|
||||
{
|
||||
// Arrange
|
||||
var server = TestServer.Create(_services, _app);
|
||||
var client = server.CreateClient();
|
||||
|
||||
// Act
|
||||
var response = await
|
||||
client.GetAsync("http://localhost/Properties" +
|
||||
"/GetCompany");
|
||||
|
||||
//Assert
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
var company = JsonConvert.DeserializeObject<Company>(
|
||||
await response.Content.ReadAsStringAsync());
|
||||
Assert.NotNull(company);
|
||||
Assert.Null(company.Employees);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("http://localhost/Home/ActionWithPersonFromUrlWithPrefix/Javier/26")]
|
||||
[InlineData("http://localhost/Home/ActionWithPersonFromUrlWithoutPrefix/Javier/26")]
|
||||
|
|
|
|||
|
|
@ -91,8 +91,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
ModelMetadata = metadataProvider.GetMetadataForType(null, typeof(int[])),
|
||||
ModelName = "someName",
|
||||
ValueProvider = valueProvider,
|
||||
ModelBinder = CreateIntBinder(),
|
||||
MetadataProvider = metadataProvider
|
||||
OperationBindingContext = new OperationBindingContext
|
||||
{
|
||||
ModelBinder = CreateIntBinder(),
|
||||
MetadataProvider = metadataProvider
|
||||
},
|
||||
};
|
||||
return bindingContext;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -123,7 +123,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
ModelMetadata = metadataProvider.GetMetadataForType(null, modelType),
|
||||
ModelName = "foo",
|
||||
ValueProvider = valueProvider,
|
||||
MetadataProvider = metadataProvider
|
||||
OperationBindingContext = new OperationBindingContext
|
||||
{
|
||||
MetadataProvider = metadataProvider
|
||||
}
|
||||
};
|
||||
return bindingContext;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
|
||||
// Assert
|
||||
Assert.True(bound);
|
||||
Assert.Equal(bindingContext.HttpContext.RequestAborted, bindingContext.Model);
|
||||
Assert.Equal(bindingContext.OperationBindingContext.HttpContext.RequestAborted, bindingContext.Model);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
|
@ -52,9 +52,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
ModelMetadata = metadataProvider.GetMetadataForType(null, modelType),
|
||||
ModelName = "someName",
|
||||
ValueProvider = new SimpleHttpValueProvider(),
|
||||
ModelBinder = new CancellationTokenModelBinder(),
|
||||
MetadataProvider = metadataProvider,
|
||||
HttpContext = new DefaultHttpContext(),
|
||||
OperationBindingContext = new OperationBindingContext
|
||||
{
|
||||
ModelBinder = new CancellationTokenModelBinder(),
|
||||
MetadataProvider = metadataProvider,
|
||||
HttpContext = new DefaultHttpContext(),
|
||||
}
|
||||
};
|
||||
|
||||
return bindingContext;
|
||||
|
|
|
|||
|
|
@ -135,7 +135,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
var bindingContext = GetModelBindingContext(new SimpleHttpValueProvider());
|
||||
|
||||
ModelValidationNode childValidationNode = null;
|
||||
Mock.Get<IModelBinder>(bindingContext.ModelBinder)
|
||||
Mock.Get<IModelBinder>(bindingContext.OperationBindingContext.ModelBinder)
|
||||
.Setup(o => o.BindModelAsync(It.IsAny<ModelBindingContext>()))
|
||||
.Returns((ModelBindingContext mbc) =>
|
||||
{
|
||||
|
|
@ -162,8 +162,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
ModelMetadata = metadataProvider.GetMetadataForType(null, typeof(int)),
|
||||
ModelName = "someName",
|
||||
ValueProvider = valueProvider,
|
||||
ModelBinder = CreateIntBinder(),
|
||||
MetadataProvider = metadataProvider
|
||||
OperationBindingContext = new OperationBindingContext
|
||||
{
|
||||
ModelBinder = CreateIntBinder(),
|
||||
MetadataProvider = metadataProvider
|
||||
}
|
||||
};
|
||||
return bindingContext;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,7 +31,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
{
|
||||
{ "someName", "dummyValue" }
|
||||
},
|
||||
ValidatorProvider = GetValidatorProvider()
|
||||
OperationBindingContext = new OperationBindingContext
|
||||
{
|
||||
ValidatorProvider = GetValidatorProvider()
|
||||
}
|
||||
};
|
||||
|
||||
var mockIntBinder = new Mock<IModelBinder>();
|
||||
|
|
@ -78,7 +81,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
{
|
||||
{ "someOtherName", "dummyValue" }
|
||||
},
|
||||
ValidatorProvider = GetValidatorProvider()
|
||||
OperationBindingContext = new OperationBindingContext
|
||||
{
|
||||
ValidatorProvider = GetValidatorProvider()
|
||||
}
|
||||
};
|
||||
|
||||
var mockIntBinder = new Mock<IModelBinder>();
|
||||
|
|
@ -151,7 +157,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
{
|
||||
FallbackToEmptyPrefix = true,
|
||||
ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(int)),
|
||||
ModelState = new ModelStateDictionary()
|
||||
ModelState = new ModelStateDictionary(),
|
||||
OperationBindingContext = Mock.Of<OperationBindingContext>(),
|
||||
};
|
||||
|
||||
// Act
|
||||
|
|
@ -306,13 +313,16 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
var metadataProvider = new DataAnnotationsModelMetadataProvider();
|
||||
var bindingContext = new ModelBindingContext
|
||||
{
|
||||
ModelBinder = binder,
|
||||
FallbackToEmptyPrefix = true,
|
||||
MetadataProvider = metadataProvider,
|
||||
ModelMetadata = metadataProvider.GetMetadataForType(null, type),
|
||||
ModelState = new ModelStateDictionary(),
|
||||
ValueProvider = valueProvider,
|
||||
ValidatorProvider = validatorProvider
|
||||
OperationBindingContext = new OperationBindingContext
|
||||
{
|
||||
MetadataProvider = metadataProvider,
|
||||
ModelBinder = binder,
|
||||
ValidatorProvider = validatorProvider
|
||||
}
|
||||
};
|
||||
return bindingContext;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,8 +25,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
{ "someName[0]", new KeyValuePair<int, string>(42, "forty-two") },
|
||||
{ "someName[1]", new KeyValuePair<int, string>(84, "eighty-four") }
|
||||
},
|
||||
ModelBinder = CreateKvpBinder(),
|
||||
MetadataProvider = metadataProvider
|
||||
OperationBindingContext = new OperationBindingContext
|
||||
{
|
||||
ModelBinder = CreateKvpBinder(),
|
||||
MetadataProvider = metadataProvider
|
||||
}
|
||||
};
|
||||
var binder = new DictionaryModelBinder<int, string>();
|
||||
|
||||
|
|
|
|||
|
|
@ -120,9 +120,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
ModelMetadata = metataProvider.GetMetadataForType(null, typeof(KeyValuePair<int, string>)),
|
||||
ModelName = "someName",
|
||||
ValueProvider = valueProvider,
|
||||
ModelBinder = innerBinder ?? CreateIntBinder(),
|
||||
MetadataProvider = metataProvider,
|
||||
ValidatorProvider = Mock.Of<IModelValidatorProvider>()
|
||||
OperationBindingContext = new OperationBindingContext
|
||||
{
|
||||
ModelBinder = innerBinder ?? CreateIntBinder(),
|
||||
MetadataProvider = metataProvider,
|
||||
ValidatorProvider = Mock.Of<IModelValidatorProvider>()
|
||||
}
|
||||
};
|
||||
return bindingContext;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,11 +21,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
ValueProvider = new SimpleHttpValueProvider()
|
||||
};
|
||||
|
||||
var newModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(object));
|
||||
|
||||
// Act
|
||||
var newBindingContext = new ModelBindingContext(originalBindingContext);
|
||||
var newBindingContext = new ModelBindingContext(originalBindingContext, string.Empty, newModelMetadata);
|
||||
|
||||
// Assert
|
||||
Assert.Null(newBindingContext.ModelMetadata);
|
||||
Assert.Same(newModelMetadata, newBindingContext.ModelMetadata);
|
||||
Assert.Equal("", newBindingContext.ModelName);
|
||||
Assert.Equal(originalBindingContext.ModelState, newBindingContext.ModelState);
|
||||
Assert.Equal(originalBindingContext.ValueProvider, newBindingContext.ValueProvider);
|
||||
|
|
|
|||
|
|
@ -17,6 +17,316 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
{
|
||||
public class MutableObjectModelBinderTest
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(typeof(Person), true)]
|
||||
[InlineData(typeof(Person), false)]
|
||||
[InlineData(typeof(EmptyModel), true)]
|
||||
[InlineData(typeof(EmptyModel), false)]
|
||||
public async Task
|
||||
CanCreateModel_CreatesModel_ForTopLevelObjectIfThereIsExplicitPrefix(Type modelType, bool isPrefixProvided)
|
||||
{
|
||||
var mockValueProvider = new Mock<IValueProvider>();
|
||||
mockValueProvider.Setup(o => o.ContainsPrefixAsync(It.IsAny<string>()))
|
||||
.Returns(Task.FromResult(false));
|
||||
|
||||
var bindingContext = new MutableObjectBinderContext
|
||||
{
|
||||
ModelBindingContext = new ModelBindingContext
|
||||
{
|
||||
// Random type.
|
||||
ModelMetadata = GetMetadataForType(typeof(Person)),
|
||||
ValueProvider = mockValueProvider.Object,
|
||||
OperationBindingContext = new OperationBindingContext
|
||||
{
|
||||
ValueProvider = mockValueProvider.Object,
|
||||
MetadataProvider = new DataAnnotationsModelMetadataProvider(),
|
||||
ValidatorProvider = Mock.Of<IModelValidatorProvider>(),
|
||||
},
|
||||
|
||||
// Setting it to empty ensures that model does not get created becasue of no model name.
|
||||
ModelName = "dummyModelName",
|
||||
}
|
||||
};
|
||||
|
||||
bindingContext.ModelBindingContext.ModelMetadata.ModelName = isPrefixProvided ? "prefix" : null;
|
||||
var mutableBinder = new TestableMutableObjectModelBinder();
|
||||
bindingContext.PropertyMetadata = mutableBinder.GetMetadataForProperties(
|
||||
bindingContext.ModelBindingContext);
|
||||
|
||||
// Act
|
||||
var retModel = await mutableBinder.CanCreateModel(bindingContext);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(isPrefixProvided, retModel);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(typeof(Person), true)]
|
||||
[InlineData(typeof(Person), false)]
|
||||
[InlineData(typeof(EmptyModel), true)]
|
||||
[InlineData(typeof(EmptyModel), false)]
|
||||
public async Task
|
||||
CanCreateModel_CreatesModel_ForTopLevelObjectIfThereIsEmptyModelName(Type modelType, bool emptyModelName)
|
||||
{
|
||||
var mockValueProvider = new Mock<IValueProvider>();
|
||||
mockValueProvider.Setup(o => o.ContainsPrefixAsync(It.IsAny<string>()))
|
||||
.Returns(Task.FromResult(false));
|
||||
|
||||
var bindingContext = new MutableObjectBinderContext
|
||||
{
|
||||
ModelBindingContext = new ModelBindingContext
|
||||
{
|
||||
// Random type.
|
||||
ModelMetadata = GetMetadataForType(typeof(Person)),
|
||||
ValueProvider = mockValueProvider.Object,
|
||||
OperationBindingContext = new OperationBindingContext
|
||||
{
|
||||
ValidatorProvider = Mock.Of<IModelValidatorProvider>(),
|
||||
ValueProvider = mockValueProvider.Object,
|
||||
MetadataProvider = new DataAnnotationsModelMetadataProvider()
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
bindingContext.ModelBindingContext.ModelName = emptyModelName ? string.Empty : "dummyModelName";
|
||||
var mutableBinder = new TestableMutableObjectModelBinder();
|
||||
bindingContext.PropertyMetadata = mutableBinder.GetMetadataForProperties(
|
||||
bindingContext.ModelBindingContext);
|
||||
|
||||
// Act
|
||||
var retModel = await mutableBinder.CanCreateModel(bindingContext);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(emptyModelName, retModel);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CanCreateModel_ReturnsFalse_ForNonTopLevelModel_IfModelIsMarkedWithBinderMetadata()
|
||||
{
|
||||
var bindingContext = new MutableObjectBinderContext
|
||||
{
|
||||
ModelBindingContext = new ModelBindingContext
|
||||
{
|
||||
// Get the property metadata so that it is not a top level object.
|
||||
ModelMetadata = GetMetadataForType(typeof(Document))
|
||||
.Properties
|
||||
.First(metadata => metadata.PropertyName == nameof(Document.SubDocument)),
|
||||
OperationBindingContext = new OperationBindingContext
|
||||
{
|
||||
ValidatorProvider = Mock.Of<IModelValidatorProvider>(),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var mutableBinder = new MutableObjectModelBinder();
|
||||
|
||||
// Act
|
||||
var canCreate = await mutableBinder.CanCreateModel(bindingContext);
|
||||
|
||||
// Assert
|
||||
Assert.False(canCreate);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CanCreateModel_ReturnsTrue_ForTopLevelModel_IfModelIsMarkedWithBinderMetadata()
|
||||
{
|
||||
var bindingContext = new MutableObjectBinderContext
|
||||
{
|
||||
ModelBindingContext = new ModelBindingContext
|
||||
{
|
||||
// Here the metadata represents a top level object.
|
||||
ModelMetadata = GetMetadataForType(typeof(Document)),
|
||||
OperationBindingContext = new OperationBindingContext
|
||||
{
|
||||
ValidatorProvider = Mock.Of<IModelValidatorProvider>(),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var mutableBinder = new MutableObjectModelBinder();
|
||||
|
||||
// Act
|
||||
var canCreate = await mutableBinder.CanCreateModel(bindingContext);
|
||||
|
||||
// Assert
|
||||
Assert.True(canCreate);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CanCreateModel_CreatesModel_IfTheModelIsBinderPoco()
|
||||
{
|
||||
var mockValueProvider = new Mock<IValueProvider>();
|
||||
mockValueProvider.Setup(o => o.ContainsPrefixAsync(It.IsAny<string>()))
|
||||
.Returns(Task.FromResult(false));
|
||||
|
||||
var bindingContext = new MutableObjectBinderContext
|
||||
{
|
||||
ModelBindingContext = new ModelBindingContext
|
||||
{
|
||||
ModelMetadata = GetMetadataForType(typeof(BinderMetadataPocoType)),
|
||||
ValueProvider = mockValueProvider.Object,
|
||||
OperationBindingContext = new OperationBindingContext
|
||||
{
|
||||
ValidatorProvider = Mock.Of<IModelValidatorProvider>(),
|
||||
ValueProvider = mockValueProvider.Object,
|
||||
MetadataProvider = new DataAnnotationsModelMetadataProvider(),
|
||||
},
|
||||
|
||||
// Setting it to empty ensures that model does not get created becasue of no model name.
|
||||
ModelName = "dummyModelName",
|
||||
},
|
||||
};
|
||||
|
||||
var mutableBinder = new TestableMutableObjectModelBinder();
|
||||
bindingContext.PropertyMetadata = mutableBinder.GetMetadataForProperties(
|
||||
bindingContext.ModelBindingContext);
|
||||
|
||||
// Act
|
||||
var retModel = await mutableBinder.CanCreateModel(bindingContext);
|
||||
|
||||
// Assert
|
||||
Assert.True(retModel);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(typeof(TypeWithNoBinderMetadata), false)]
|
||||
[InlineData(typeof(TypeWithNoBinderMetadata), true)]
|
||||
[InlineData(typeof(TypeWithAtLeastOnePropertyMarkedUsingValueBinderMetadata), false)]
|
||||
[InlineData(typeof(TypeWithAtLeastOnePropertyMarkedUsingValueBinderMetadata), true)]
|
||||
[InlineData(typeof(TypeWithUnmarkedAndBinderMetadataMarkedProperties), false)]
|
||||
[InlineData(typeof(TypeWithUnmarkedAndBinderMetadataMarkedProperties), true)]
|
||||
public async Task
|
||||
CanCreateModel_CreatesModelForValueProviderBasedBinderMetadatas_IfAValueProviderProvidesValue
|
||||
(Type modelType, bool valueProviderProvidesValue)
|
||||
{
|
||||
var mockValueProvider = new Mock<IValueProvider>();
|
||||
mockValueProvider.Setup(o => o.ContainsPrefixAsync(It.IsAny<string>()))
|
||||
.Returns(Task.FromResult(valueProviderProvidesValue));
|
||||
|
||||
var bindingContext = new MutableObjectBinderContext
|
||||
{
|
||||
ModelBindingContext = new ModelBindingContext
|
||||
{
|
||||
ModelMetadata = GetMetadataForType(modelType),
|
||||
ValueProvider = mockValueProvider.Object,
|
||||
OperationBindingContext = new OperationBindingContext
|
||||
{
|
||||
ValidatorProvider = Mock.Of<IModelValidatorProvider>(),
|
||||
ValueProvider = mockValueProvider.Object,
|
||||
MetadataProvider = new DataAnnotationsModelMetadataProvider(),
|
||||
},
|
||||
// Setting it to empty ensures that model does not get created becasue of no model name.
|
||||
ModelName = "dummyName"
|
||||
}
|
||||
};
|
||||
|
||||
var mutableBinder = new TestableMutableObjectModelBinder();
|
||||
bindingContext.PropertyMetadata = mutableBinder.GetMetadataForProperties(
|
||||
bindingContext.ModelBindingContext);
|
||||
|
||||
// Act
|
||||
var retModel = await mutableBinder.CanCreateModel(bindingContext);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(valueProviderProvidesValue, retModel);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(typeof(TypeWithAtLeastOnePropertyMarkedUsingValueBinderMetadata), false)]
|
||||
[InlineData(typeof(TypeWithAtLeastOnePropertyMarkedUsingValueBinderMetadata), true)]
|
||||
public async Task CanCreateModel_ForExplicitValueProviderMetadata_UsesOriginalValueProvider(Type modelType, bool originalValueProviderProvidesValue)
|
||||
{
|
||||
var mockValueProvider = new Mock<IValueProvider>();
|
||||
mockValueProvider.Setup(o => o.ContainsPrefixAsync(It.IsAny<string>()))
|
||||
.Returns(Task.FromResult(false));
|
||||
|
||||
var mockOriginalValueProvider = new Mock<IMetadataAwareValueProvider>();
|
||||
mockOriginalValueProvider.Setup(o => o.ContainsPrefixAsync(It.IsAny<string>()))
|
||||
.Returns(Task.FromResult(originalValueProviderProvidesValue));
|
||||
mockOriginalValueProvider.Setup(o => o.Filter(It.IsAny<IValueProviderMetadata>()))
|
||||
.Returns<IValueProviderMetadata>(
|
||||
valueProviderMetadata =>
|
||||
{
|
||||
if(valueProviderMetadata is ValueBinderMetadataAttribute)
|
||||
{
|
||||
return mockOriginalValueProvider.Object;
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
var bindingContext = new MutableObjectBinderContext
|
||||
{
|
||||
ModelBindingContext = new ModelBindingContext
|
||||
{
|
||||
ModelMetadata = GetMetadataForType(modelType),
|
||||
ValueProvider = mockValueProvider.Object,
|
||||
OperationBindingContext = new OperationBindingContext
|
||||
{
|
||||
ValueProvider = mockOriginalValueProvider.Object,
|
||||
MetadataProvider = new DataAnnotationsModelMetadataProvider(),
|
||||
ValidatorProvider = Mock.Of<IModelValidatorProvider>(),
|
||||
},
|
||||
|
||||
// Setting it to empty ensures that model does not get created becasue of no model name.
|
||||
ModelName = "dummyName"
|
||||
}
|
||||
};
|
||||
|
||||
var mutableBinder = new TestableMutableObjectModelBinder();
|
||||
bindingContext.PropertyMetadata = mutableBinder.GetMetadataForProperties(
|
||||
bindingContext.ModelBindingContext);
|
||||
|
||||
// Act
|
||||
var retModel = await mutableBinder.CanCreateModel(bindingContext);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(originalValueProviderProvidesValue, retModel);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(typeof(TypeWithUnmarkedAndBinderMetadataMarkedProperties), false)]
|
||||
[InlineData(typeof(TypeWithUnmarkedAndBinderMetadataMarkedProperties), true)]
|
||||
[InlineData(typeof(TypeWithNoBinderMetadata), false)]
|
||||
[InlineData(typeof(TypeWithNoBinderMetadata), true)]
|
||||
public async Task CanCreateModel_UnmarkedProperties_UsesCurrentValueProvider(Type modelType, bool valueProviderProvidesValue)
|
||||
{
|
||||
var mockValueProvider = new Mock<IValueProvider>();
|
||||
mockValueProvider.Setup(o => o.ContainsPrefixAsync(It.IsAny<string>()))
|
||||
.Returns(Task.FromResult(valueProviderProvidesValue));
|
||||
|
||||
var mockOriginalValueProvider = new Mock<IValueProvider>();
|
||||
mockOriginalValueProvider.Setup(o => o.ContainsPrefixAsync(It.IsAny<string>()))
|
||||
.Returns(Task.FromResult(false));
|
||||
|
||||
var bindingContext = new MutableObjectBinderContext
|
||||
{
|
||||
ModelBindingContext = new ModelBindingContext
|
||||
{
|
||||
ModelMetadata = GetMetadataForType(modelType),
|
||||
ValueProvider = mockValueProvider.Object,
|
||||
OperationBindingContext = new OperationBindingContext
|
||||
{
|
||||
ValidatorProvider = Mock.Of<IModelValidatorProvider>(),
|
||||
ValueProvider = mockOriginalValueProvider.Object,
|
||||
MetadataProvider = new DataAnnotationsModelMetadataProvider(),
|
||||
},
|
||||
// Setting it to empty ensures that model does not get created becasue of no model name.
|
||||
ModelName = "dummyName"
|
||||
}
|
||||
};
|
||||
|
||||
var mutableBinder = new TestableMutableObjectModelBinder();
|
||||
bindingContext.PropertyMetadata = mutableBinder.GetMetadataForProperties(
|
||||
bindingContext.ModelBindingContext);
|
||||
|
||||
// Act
|
||||
var retModel = await mutableBinder.CanCreateModel(bindingContext);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(valueProviderProvidesValue, retModel);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task BindModel_InitsInstance()
|
||||
{
|
||||
|
|
@ -31,9 +341,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
ModelMetadata = GetMetadataForObject(new Person()),
|
||||
ModelName = "someName",
|
||||
ValueProvider = mockValueProvider.Object,
|
||||
ModelBinder = mockDtoBinder.Object,
|
||||
MetadataProvider = new DataAnnotationsModelMetadataProvider(),
|
||||
ValidatorProvider = Mock.Of<IModelValidatorProvider>()
|
||||
OperationBindingContext = new OperationBindingContext
|
||||
{
|
||||
ModelBinder = mockDtoBinder.Object,
|
||||
MetadataProvider = new DataAnnotationsModelMetadataProvider(),
|
||||
ValidatorProvider = Mock.Of<IModelValidatorProvider>()
|
||||
}
|
||||
};
|
||||
|
||||
mockDtoBinder
|
||||
|
|
@ -46,8 +359,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
|
||||
var testableBinder = new Mock<TestableMutableObjectModelBinder> { CallBase = true };
|
||||
testableBinder.Setup(o => o.EnsureModelPublic(bindingContext)).Verifiable();
|
||||
testableBinder.Setup(o => o.GetMetadataForPropertiesPublic(bindingContext))
|
||||
.Returns(new ModelMetadata[0]).Verifiable();
|
||||
testableBinder.Setup(o => o.GetMetadataForProperties(bindingContext))
|
||||
.Returns(new ModelMetadata[0]);
|
||||
|
||||
// Act
|
||||
var retValue = await testableBinder.Object.BindModelAsync(bindingContext);
|
||||
|
|
@ -73,9 +386,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
ModelMetadata = GetMetadataForObject(new Person()),
|
||||
ModelName = "",
|
||||
ValueProvider = mockValueProvider.Object,
|
||||
ModelBinder = mockDtoBinder.Object,
|
||||
MetadataProvider = new DataAnnotationsModelMetadataProvider(),
|
||||
ValidatorProvider = Mock.Of<IModelValidatorProvider>()
|
||||
OperationBindingContext = new OperationBindingContext
|
||||
{
|
||||
ModelBinder = mockDtoBinder.Object,
|
||||
MetadataProvider = new DataAnnotationsModelMetadataProvider(),
|
||||
ValidatorProvider = Mock.Of<IModelValidatorProvider>()
|
||||
}
|
||||
};
|
||||
|
||||
mockDtoBinder
|
||||
|
|
@ -88,8 +404,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
|
||||
var testableBinder = new Mock<TestableMutableObjectModelBinder> { CallBase = true };
|
||||
testableBinder.Setup(o => o.EnsureModelPublic(bindingContext)).Verifiable();
|
||||
testableBinder.Setup(o => o.GetMetadataForPropertiesPublic(bindingContext))
|
||||
.Returns(new ModelMetadata[0]).Verifiable();
|
||||
testableBinder.Setup(o => o.GetMetadataForProperties(bindingContext))
|
||||
.Returns(new ModelMetadata[0]);
|
||||
|
||||
// Act
|
||||
var retValue = await testableBinder.Object.BindModelAsync(bindingContext);
|
||||
|
|
@ -237,14 +553,17 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
var bindingContext = new ModelBindingContext
|
||||
{
|
||||
ModelMetadata = GetMetadataForType(typeof(PersonWithBindExclusion)),
|
||||
ValidatorProvider = Mock.Of<IModelValidatorProvider>(),
|
||||
MetadataProvider = new DataAnnotationsModelMetadataProvider()
|
||||
OperationBindingContext = new OperationBindingContext
|
||||
{
|
||||
ValidatorProvider = Mock.Of<IModelValidatorProvider>(),
|
||||
MetadataProvider = new DataAnnotationsModelMetadataProvider()
|
||||
}
|
||||
};
|
||||
|
||||
var testableBinder = new TestableMutableObjectModelBinder();
|
||||
|
||||
// Act
|
||||
var propertyMetadatas = testableBinder.GetMetadataForPropertiesPublic(bindingContext);
|
||||
var propertyMetadatas = testableBinder.GetMetadataForProperties(bindingContext);
|
||||
var returnedPropertyNames = propertyMetadatas.Select(o => o.PropertyName).ToArray();
|
||||
|
||||
// Assert
|
||||
|
|
@ -267,14 +586,17 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
var bindingContext = new ModelBindingContext
|
||||
{
|
||||
ModelMetadata = GetMetadataForType(typeof(Person)),
|
||||
ValidatorProvider = Mock.Of<IModelValidatorProvider>(),
|
||||
MetadataProvider = new DataAnnotationsModelMetadataProvider()
|
||||
OperationBindingContext = new OperationBindingContext
|
||||
{
|
||||
ValidatorProvider = Mock.Of<IModelValidatorProvider>(),
|
||||
MetadataProvider = new DataAnnotationsModelMetadataProvider()
|
||||
}
|
||||
};
|
||||
|
||||
var testableBinder = new TestableMutableObjectModelBinder();
|
||||
|
||||
// Act
|
||||
var propertyMetadatas = testableBinder.GetMetadataForPropertiesPublic(bindingContext);
|
||||
var propertyMetadatas = testableBinder.GetMetadataForProperties(bindingContext);
|
||||
var returnedPropertyNames = propertyMetadatas.Select(o => o.PropertyName).ToArray();
|
||||
|
||||
// Assert
|
||||
|
|
@ -300,14 +622,17 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
var bindingContext = new ModelBindingContext
|
||||
{
|
||||
ModelMetadata = GetMetadataForType(typeof(TypeWithExcludedPropertiesUsingBindAttribute)),
|
||||
ValidatorProvider = Mock.Of<IModelValidatorProvider>(),
|
||||
MetadataProvider = new DataAnnotationsModelMetadataProvider()
|
||||
OperationBindingContext = new OperationBindingContext
|
||||
{
|
||||
ValidatorProvider = Mock.Of<IModelValidatorProvider>(),
|
||||
MetadataProvider = new DataAnnotationsModelMetadataProvider()
|
||||
}
|
||||
};
|
||||
|
||||
var testableBinder = new TestableMutableObjectModelBinder();
|
||||
|
||||
// Act
|
||||
var propertyMetadatas = testableBinder.GetMetadataForPropertiesPublic(bindingContext);
|
||||
var propertyMetadatas = testableBinder.GetMetadataForProperties(bindingContext);
|
||||
var returnedPropertyNames = propertyMetadatas.Select(o => o.PropertyName).ToArray();
|
||||
|
||||
// Assert
|
||||
|
|
@ -334,14 +659,17 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
var bindingContext = new ModelBindingContext
|
||||
{
|
||||
ModelMetadata = GetMetadataForType(typeof(TypeWithIncludedPropertiesUsingBindAttribute)),
|
||||
ValidatorProvider = Mock.Of<IModelValidatorProvider>(),
|
||||
MetadataProvider = new DataAnnotationsModelMetadataProvider()
|
||||
OperationBindingContext = new OperationBindingContext
|
||||
{
|
||||
ValidatorProvider = Mock.Of<IModelValidatorProvider>(),
|
||||
MetadataProvider = new DataAnnotationsModelMetadataProvider()
|
||||
}
|
||||
};
|
||||
|
||||
var testableBinder = new TestableMutableObjectModelBinder();
|
||||
|
||||
// Act
|
||||
var propertyMetadatas = testableBinder.GetMetadataForPropertiesPublic(bindingContext);
|
||||
var propertyMetadatas = testableBinder.GetMetadataForProperties(bindingContext);
|
||||
var returnedPropertyNames = propertyMetadatas.Select(o => o.PropertyName).ToArray();
|
||||
|
||||
// Assert
|
||||
|
|
@ -355,7 +683,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
var bindingContext = new ModelBindingContext
|
||||
{
|
||||
ModelMetadata = GetMetadataForObject(new ModelWithMixedBindingBehaviors()),
|
||||
ValidatorProvider = Mock.Of<IModelValidatorProvider>()
|
||||
OperationBindingContext = new OperationBindingContext
|
||||
{
|
||||
ValidatorProvider = Mock.Of<IModelValidatorProvider>()
|
||||
}
|
||||
};
|
||||
|
||||
// Act
|
||||
|
|
@ -429,7 +760,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
{
|
||||
ModelMetadata = containerMetadata,
|
||||
ModelName = "theModel",
|
||||
ValidatorProvider = Mock.Of<IModelValidatorProvider>()
|
||||
OperationBindingContext = new OperationBindingContext
|
||||
{
|
||||
ValidatorProvider = Mock.Of<IModelValidatorProvider>()
|
||||
}
|
||||
};
|
||||
var dto = new ComplexModelDto(containerMetadata, containerMetadata.Properties);
|
||||
|
||||
|
|
@ -472,10 +806,14 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
ModelMetadata = containerMetadata,
|
||||
ModelName = "theModel",
|
||||
ModelState = new ModelStateDictionary(),
|
||||
ValidatorProvider = Mock.Of<IModelValidatorProvider>()
|
||||
OperationBindingContext = new OperationBindingContext
|
||||
{
|
||||
ValidatorProvider = Mock.Of<IModelValidatorProvider>()
|
||||
}
|
||||
};
|
||||
var validationContext = new ModelValidationContext(new EmptyModelMetadataProvider(),
|
||||
bindingContext.ValidatorProvider,
|
||||
bindingContext.OperationBindingContext
|
||||
.ValidatorProvider,
|
||||
bindingContext.ModelState,
|
||||
containerMetadata,
|
||||
null);
|
||||
|
|
@ -705,7 +1043,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
var propertyMetadata = bindingContext.ModelMetadata.Properties.First(o => o.PropertyName == "PropertyWithDefaultValue");
|
||||
var validationNode = new ModelValidationNode(propertyMetadata, "foo");
|
||||
var dtoResult = new ComplexModelDtoResult(model: null, validationNode: validationNode);
|
||||
var requiredValidator = bindingContext.ValidatorProvider
|
||||
var requiredValidator = bindingContext.OperationBindingContext
|
||||
.ValidatorProvider
|
||||
.GetValidators(propertyMetadata)
|
||||
.FirstOrDefault(v => v.IsRequired);
|
||||
|
||||
|
|
@ -748,7 +1087,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
var propertyMetadata = bindingContext.ModelMetadata.Properties.Single(o => o.PropertyName == "DateOfBirth");
|
||||
var validationNode = new ModelValidationNode(propertyMetadata, "foo");
|
||||
var dtoResult = new ComplexModelDtoResult(new DateTime(2001, 1, 1), validationNode);
|
||||
var requiredValidator = bindingContext.ValidatorProvider
|
||||
var requiredValidator = bindingContext.OperationBindingContext
|
||||
.ValidatorProvider
|
||||
.GetValidators(propertyMetadata)
|
||||
.FirstOrDefault(v => v.IsRequired);
|
||||
var validationContext = new ModelValidationContext(bindingContext, propertyMetadata);
|
||||
|
|
@ -895,13 +1235,17 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
ModelState = new ModelStateDictionary(),
|
||||
ModelMetadata = metadata,
|
||||
ModelName = "theModel",
|
||||
ValidatorProvider = new CompositeModelValidatorProvider(provider.Object)
|
||||
OperationBindingContext = new OperationBindingContext
|
||||
{
|
||||
ValidatorProvider = new CompositeModelValidatorProvider(provider.Object)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static IModelValidator GetRequiredValidator(ModelBindingContext bindingContext, ModelMetadata propertyMetadata)
|
||||
{
|
||||
return bindingContext.ValidatorProvider
|
||||
return bindingContext.OperationBindingContext
|
||||
.ValidatorProvider
|
||||
.GetValidators(propertyMetadata)
|
||||
.FirstOrDefault(v => v.IsRequired);
|
||||
}
|
||||
|
|
@ -934,6 +1278,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
binderMetadata: null);
|
||||
}
|
||||
|
||||
private class EmptyModel
|
||||
{
|
||||
}
|
||||
|
||||
private class Person
|
||||
{
|
||||
private DateTime? _dateOfDeath;
|
||||
|
|
@ -1033,6 +1381,53 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
}
|
||||
}
|
||||
|
||||
private class TypeWithNoBinderMetadata
|
||||
{
|
||||
public int UnMarkedProperty { get; set; }
|
||||
}
|
||||
|
||||
private class BinderMetadataPocoType
|
||||
{
|
||||
[NonValueBinderMetadata]
|
||||
public string MarkedWithABinderMetadata { get; set; }
|
||||
}
|
||||
|
||||
// Not a Metadata poco because there is a property with value binder Metadata.
|
||||
private class TypeWithAtLeastOnePropertyMarkedUsingValueBinderMetadata
|
||||
{
|
||||
[NonValueBinderMetadata]
|
||||
public string MarkedWithABinderMetadata { get; set; }
|
||||
|
||||
[ValueBinderMetadata]
|
||||
public string MarkedWithAValueBinderMetadata { get; set; }
|
||||
}
|
||||
|
||||
// not a Metadata poco because there is an unmarked property.
|
||||
private class TypeWithUnmarkedAndBinderMetadataMarkedProperties
|
||||
{
|
||||
public int UnmarkedProperty { get; set; }
|
||||
|
||||
[NonValueBinderMetadata]
|
||||
public string MarkedWithABinderMetadata { get; set; }
|
||||
}
|
||||
|
||||
public class Document
|
||||
{
|
||||
[NonValueBinderMetadata]
|
||||
public string Version { get; set; }
|
||||
|
||||
[NonValueBinderMetadata]
|
||||
public Document SubDocument { get; set; }
|
||||
}
|
||||
|
||||
private class NonValueBinderMetadataAttribute : Attribute, IBinderMetadata
|
||||
{
|
||||
}
|
||||
|
||||
private class ValueBinderMetadataAttribute : Attribute, IValueProviderMetadata
|
||||
{
|
||||
}
|
||||
|
||||
public class TestableMutableObjectModelBinder : MutableObjectModelBinder
|
||||
{
|
||||
public virtual bool CanUpdatePropertyPublic(ModelMetadata propertyMetadata)
|
||||
|
|
@ -1065,16 +1460,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
EnsureModelPublic(bindingContext);
|
||||
}
|
||||
|
||||
public virtual IEnumerable<ModelMetadata> GetMetadataForPropertiesPublic(ModelBindingContext bindingContext)
|
||||
public virtual new IEnumerable<ModelMetadata> GetMetadataForProperties(ModelBindingContext bindingContext)
|
||||
{
|
||||
return base.GetMetadataForProperties(bindingContext);
|
||||
}
|
||||
|
||||
protected override IEnumerable<ModelMetadata> GetMetadataForProperties(ModelBindingContext bindingContext)
|
||||
{
|
||||
return GetMetadataForPropertiesPublic(bindingContext);
|
||||
}
|
||||
|
||||
public virtual void SetPropertyPublic(ModelBindingContext bindingContext,
|
||||
ModelMetadata propertyMetadata,
|
||||
ComplexModelDtoResult dtoResult,
|
||||
|
|
|
|||
|
|
@ -95,6 +95,52 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetMetadataForProperty_WithNoBinderMetadata_GetsItFromType()
|
||||
{
|
||||
// Arrange
|
||||
var provider = new DataAnnotationsModelMetadataProvider();
|
||||
|
||||
// Act
|
||||
var propertyMetadata = provider.GetMetadataForProperty(null, typeof(Person), nameof(Person.Parent));
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(propertyMetadata.BinderMetadata);
|
||||
Assert.IsType<TestBinderMetadataAttribute>(propertyMetadata.BinderMetadata);
|
||||
}
|
||||
|
||||
#if ASPNET50
|
||||
[Fact]
|
||||
public void GetMetadataForParameter_WithNoBinderMetadata_GetsItFromType()
|
||||
{
|
||||
// Arrange
|
||||
var provider = new DataAnnotationsModelMetadataProvider();
|
||||
|
||||
// Act
|
||||
var parameterMetadata = provider.GetMetadataForParameter(null,
|
||||
typeof(Person).GetMethod("Update"),
|
||||
"person",
|
||||
null);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(parameterMetadata.BinderMetadata);
|
||||
Assert.IsType<TestBinderMetadataAttribute>(parameterMetadata.BinderMetadata);
|
||||
}
|
||||
#endif
|
||||
public class TestBinderMetadataAttribute : Attribute, IBinderMetadata
|
||||
{
|
||||
}
|
||||
|
||||
[TestBinderMetadata]
|
||||
public class Person
|
||||
{
|
||||
public Person Parent { get; set; }
|
||||
|
||||
public void Update(Person person)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
// GetMetadataForProperty
|
||||
|
||||
[Fact]
|
||||
|
|
|
|||
|
|
@ -30,6 +30,10 @@ namespace FiltersWebSite
|
|||
|
||||
public override void OnActionExecuting(ActionExecutingContext context)
|
||||
{
|
||||
if (context.ActionArguments["fromGlobalActionFilter"] == null)
|
||||
{
|
||||
context.ActionArguments["fromGlobalActionFilter"] = new List<ContentResult>();
|
||||
}
|
||||
(context.ActionArguments["fromGlobalActionFilter"] as List<ContentResult>)
|
||||
.Add(Helpers.GetContentResult(context.Result, "Controller override - OnActionExecuting"));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,70 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNet.Mvc;
|
||||
|
||||
namespace ModelBindingWebSite.Controllers
|
||||
{
|
||||
public class FromAttributesController : Controller
|
||||
{
|
||||
[Route("/FromAttributes/[action]/{HomeAddress.Street}/{HomeAddress.State}/{HomeAddress.Zip}")]
|
||||
public User_FromBody GetUser_FromBody(User_FromBody user)
|
||||
{
|
||||
return user;
|
||||
}
|
||||
|
||||
[Route("/FromAttributes/[action]/{HomeAddress.Street}/{HomeAddress.State}/{HomeAddress.Zip}")]
|
||||
public User_FromForm GetUser_FromForm(User_FromForm user)
|
||||
{
|
||||
return user;
|
||||
}
|
||||
|
||||
[Route("/FromAttributes/[action]/{HomeAddress.Street}/{HomeAddress.State}/{HomeAddress.Zip}")]
|
||||
public User_FromForm GetUser([FromRoute] Address homeAddress,
|
||||
[FromForm] Address officeAddress,
|
||||
[FromQuery] Address shippingAddress)
|
||||
{
|
||||
return new User_FromForm
|
||||
{
|
||||
HomeAddress = homeAddress,
|
||||
OfficeAddress = officeAddress,
|
||||
ShippingAddress = shippingAddress
|
||||
};
|
||||
}
|
||||
|
||||
public User_FromForm MultipleFromFormParameters([FromForm] Address homeAddress,
|
||||
[FromForm] Address officeAddress)
|
||||
{
|
||||
return new User_FromForm
|
||||
{
|
||||
HomeAddress = homeAddress,
|
||||
OfficeAddress = officeAddress,
|
||||
};
|
||||
}
|
||||
|
||||
// User_FromForm has a FromForm property.
|
||||
public User_FromForm MultipleFromFormParameterAndProperty(User_FromForm user,
|
||||
[FromForm] Address defaultAddress)
|
||||
{
|
||||
user.HomeAddress = defaultAddress;
|
||||
return user;
|
||||
}
|
||||
|
||||
public void FromBodyParametersThrows([FromBody] int id, [FromBody] string emp)
|
||||
{
|
||||
}
|
||||
|
||||
// Customer has a FromBody Property.
|
||||
public void FromBodyParameterAndPropertyThrows([FromBody] Person p, Customer customer)
|
||||
{
|
||||
}
|
||||
|
||||
public void FormAndBody_Throws([FromForm] Person p, Customer customer)
|
||||
{
|
||||
}
|
||||
|
||||
public void FormAndBody_AsParameters_Throws([FromBody] int id, [FromForm] string emp)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -49,6 +49,26 @@ namespace ModelBindingWebSite.Controllers
|
|||
return person;
|
||||
}
|
||||
|
||||
public Customer GetCustomer(int id)
|
||||
{
|
||||
var customer = CreateCustomer(id);
|
||||
|
||||
// should update customer.Department from body.
|
||||
TryUpdateModelAsync(customer);
|
||||
|
||||
return customer;
|
||||
}
|
||||
|
||||
private Customer CreateCustomer(int id)
|
||||
{
|
||||
return new Customer()
|
||||
{
|
||||
Id = id,
|
||||
Name = "dummy",
|
||||
Age = 25,
|
||||
};
|
||||
}
|
||||
|
||||
private Dictionary<string, string> CreateValidationDictionary()
|
||||
{
|
||||
var result = new Dictionary<string, string>();
|
||||
|
|
|
|||
|
|
@ -1,14 +1,16 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.AspNet.Mvc;
|
||||
|
||||
namespace ModelBindingWebSite.Controllers
|
||||
{
|
||||
public class MultipleParametersFromBodyController : Controller
|
||||
public class PropertiesController : Controller
|
||||
{
|
||||
public void MultipleParametersFromBodyThrows([FromBody] int i, [FromBody] string emp)
|
||||
public Company GetCompany(Company c)
|
||||
{
|
||||
return c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,9 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.AspNet.Mvc;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||
|
||||
namespace ModelBindingWebSite.Controllers
|
||||
{
|
||||
|
|
@ -25,5 +27,10 @@ namespace ModelBindingWebSite.Controllers
|
|||
{
|
||||
return emp;
|
||||
}
|
||||
|
||||
public Document EchoDocument(Document poco)
|
||||
{
|
||||
return poco;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +1,12 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||
|
||||
namespace ModelBindingWebSite
|
||||
{
|
||||
public class ExternalType
|
||||
public class FromNonExistantBinderAttribute : Attribute, IBinderMetadata
|
||||
{
|
||||
public string Department { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||
|
||||
namespace ModelBindingWebSite
|
||||
{
|
||||
public class FromTestAttribute : Attribute, IBinderMetadata
|
||||
{
|
||||
public object Value { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
namespace ModelBindingWebSite
|
||||
{
|
||||
public class Address
|
||||
{
|
||||
public int Street { get; set; }
|
||||
public string State { get; set; }
|
||||
public int Zip { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ModelBindingWebSite
|
||||
{
|
||||
public class Company
|
||||
{
|
||||
public Department Department { get; set; }
|
||||
|
||||
[FromTest]
|
||||
public Person CEO { get; set; }
|
||||
|
||||
public IList<Employee> Employees { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNet.Mvc;
|
||||
|
||||
namespace ModelBindingWebSite
|
||||
{
|
||||
public class Customer : Person
|
||||
{
|
||||
public int Id { get; set; }
|
||||
|
||||
[FromBody]
|
||||
public Department Department { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
namespace ModelBindingWebSite
|
||||
{
|
||||
public class Department
|
||||
{
|
||||
// A single property marked with a binder metadata attribute makes it a binder metadata poco.
|
||||
[FromTest]
|
||||
public string Name { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
namespace ModelBindingWebSite
|
||||
{
|
||||
public class Document
|
||||
{
|
||||
[FromNonExistantBinder]
|
||||
public string Version { get; set; }
|
||||
|
||||
[FromNonExistantBinder]
|
||||
public Document SubDocument { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNet.Mvc;
|
||||
|
||||
namespace ModelBindingWebSite
|
||||
{
|
||||
public class User_FromBody
|
||||
{
|
||||
[FromRoute]
|
||||
public Address HomeAddress { get; set; }
|
||||
|
||||
[FromBody]
|
||||
public Address OfficeAddress { get; set; }
|
||||
|
||||
[FromQuery]
|
||||
public Address ShippingAddress { get; set; }
|
||||
|
||||
// Should get it from the first value provider which
|
||||
// can provide values for this.
|
||||
public Address DefaultAddress { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNet.Mvc;
|
||||
|
||||
namespace ModelBindingWebSite
|
||||
{
|
||||
public class User_FromForm
|
||||
{
|
||||
[FromRoute]
|
||||
public Address HomeAddress { get; set; }
|
||||
|
||||
[FromForm]
|
||||
public Address OfficeAddress { get; set; }
|
||||
|
||||
[FromQuery]
|
||||
public Address ShippingAddress { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -22,6 +22,7 @@ namespace ModelBindingWebSite
|
|||
.Configure<MvcOptions>(m =>
|
||||
{
|
||||
m.MaxModelValidationErrors = 8;
|
||||
m.ModelBinders.Insert(0, typeof(TestMetadataAwareBinder));
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||
|
||||
namespace ModelBindingWebSite
|
||||
{
|
||||
public class TestMetadataAwareBinder : MetadataAwareBinder<FromTestAttribute>
|
||||
{
|
||||
protected override Task<bool> BindAsync(ModelBindingContext bindingContext, FromTestAttribute metadata)
|
||||
{
|
||||
bindingContext.Model = metadata.Value;
|
||||
|
||||
if (!IsSimpleType(bindingContext.ModelType))
|
||||
{
|
||||
bindingContext.Model = Activator.CreateInstance(bindingContext.ModelType);
|
||||
}
|
||||
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
|
||||
private bool IsSimpleType(Type type)
|
||||
{
|
||||
return type.GetTypeInfo().IsPrimitive ||
|
||||
type.Equals(typeof(decimal)) ||
|
||||
type.Equals(typeof(string)) ||
|
||||
type.Equals(typeof(DateTime)) ||
|
||||
type.Equals(typeof(Guid)) ||
|
||||
type.Equals(typeof(DateTimeOffset)) ||
|
||||
type.Equals(typeof(TimeSpan));
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue