Add `ModelState` entries for greedy and type-matching model binders

- part II of II for #2445
- `FormCollectionModelBinder` is an exception because container is not user-provided
 - no `ModelState` entry added
- enable tests that #2445 was blocking
 - fix these and other tests expecting different `ModelState` entries
- simplify logic in `FormFileModelBinder`

`ValueProviderResult`
- remove `protected` setters and parameterless constructor
 - no scenario for their use in subclasses; however `ConvertTo()` remains `virtual`
- add single-parameter constructor
 - use in most of the greedy and type-matching model binders
- add doc comments throughout class

nits:
- use new `ValueProviderResult` constructor in many existing tests
- `""` -> `string.Empty` and `vpr` -> `valueProviderResult` in `ValueProviderResultTest`
- improve some test names in `BodyValidationIntegrationTests`
- do not check `Message` of a Json.NET `Exception`
This commit is contained in:
Doug Bunting 2015-06-23 22:34:55 -07:00
parent 4a4b8ec87e
commit 715a0b6021
20 changed files with 357 additions and 345 deletions

View File

@ -12,45 +12,83 @@ using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.ModelBinding namespace Microsoft.AspNet.Mvc.ModelBinding
{ {
/// <summary>
/// Result of an <see cref="IValueProvider.GetValueAsync"/> operation.
/// </summary>
public class ValueProviderResult public class ValueProviderResult
{ {
private static readonly CultureInfo _staticCulture = CultureInfo.InvariantCulture; private static readonly CultureInfo _staticCulture = CultureInfo.InvariantCulture;
private CultureInfo _instanceCulture;
// default constructor so that subclassed types can set the properties themselves /// <summary>
protected ValueProviderResult() /// Instantiates a new instance of the <see cref="ValueProviderResult"/> class with given
/// <paramref name="rawValue"/>. Initializes <see cref="Culture"/> to
/// <see cref="CultureInfo.InvariantCulture"/>.
/// </summary>
/// <param name="rawValue">The <see cref="RawValue"/> value of the new instance.</param>
public ValueProviderResult(object rawValue)
: this(rawValue, attemptedValue: null, culture: _staticCulture)
{ {
} }
/// <summary>
/// Instantiates a new instance of the <see cref="ValueProviderResult"/> class with given
/// <paramref name="rawValue"/>, <paramref name="attemptedValue"/>, and <paramref name="culture"/>.
/// </summary>
/// <param name="rawValue">The <see cref="RawValue"/> value of the new instance.</param>
/// <param name="attemptedValue">The <see cref="AttemptedValue"/> value of the new instance.</param>
/// <param name="culture">The <see cref="Culture"/> value of the new instance.</param>
public ValueProviderResult(object rawValue, string attemptedValue, CultureInfo culture) public ValueProviderResult(object rawValue, string attemptedValue, CultureInfo culture)
{ {
RawValue = rawValue; RawValue = rawValue;
AttemptedValue = attemptedValue; AttemptedValue = attemptedValue;
Culture = culture; Culture = culture ?? _staticCulture;
} }
public string AttemptedValue { get; protected set; } /// <summary>
/// <see cref="string"/> conversion of <see cref="RawValue"/>.
/// </summary>
/// <remarks>
/// Used in helpers that generate <c>&lt;textarea&gt;</c> elements as well as some error messages.
/// </remarks>
public string AttemptedValue { get; }
public CultureInfo Culture /// <summary>
{ /// <see cref="CultureInfo"/> to use in <see cref="ConvertTo(Type)"/> or
get /// <see cref="ConvertTo(Type, CultureInfo)"/> if passed <see cref="CultureInfo"/> is <c>null</c>.
{ /// </summary>
if (_instanceCulture == null) public CultureInfo Culture { get; }
{
_instanceCulture = _staticCulture;
}
return _instanceCulture;
}
protected set { _instanceCulture = value; }
}
public object RawValue { get; protected set; } /// <summary>
/// The provided <see cref="object"/>.
/// </summary>
public object RawValue { get; }
/// <summary>
/// Converts <see cref="RawValue"/> to the given <paramref name="type"/>. Uses <see cref="Culture"/> for
/// <see cref="TypeConverter"/> operations.
/// </summary>
/// <param name="type">The target <see cref="Type"/> of the conversion.</param>
/// <returns>
/// <see cref="RawValue"/> converted to the given <paramref name="type"/>. <c>null</c> if the conversion fails.
/// </returns>
public object ConvertTo(Type type) public object ConvertTo(Type type)
{ {
return ConvertTo(type, culture: null); return ConvertTo(type, culture: null);
} }
/// <summary>
/// Converts <see cref="RawValue"/> to the given <paramref name="type"/> using the given
/// <paramref name="culture"/>.
/// </summary>
/// <param name="type">The target <see cref="Type"/> of the conversion.</param>
/// <param name="culture">
/// The <see cref="CultureInfo"/> to use for <see cref="TypeConverter"/> operations. Uses
/// <see cref="Culture"/> if this parameter is <c>null</c>.
/// </param>
/// <returns>
/// <see cref="RawValue"/> converted to the given <paramref name="type"/> using the given
/// <paramref name="culture"/>. <c>null</c> if the conversion fails.
/// </returns>
public virtual object ConvertTo([NotNull] Type type, CultureInfo culture) public virtual object ConvertTo([NotNull] Type type, CultureInfo culture)
{ {
var value = RawValue; var value = RawValue;

View File

@ -66,6 +66,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
return new ModelBindingResult(modelBindingKey); return new ModelBindingResult(modelBindingKey);
} }
var valueProviderResult = new ValueProviderResult(rawValue: model);
bindingContext.ModelState.SetModelValue(modelBindingKey, valueProviderResult);
var validationNode = new ModelValidationNode(modelBindingKey, bindingContext.ModelMetadata, model) var validationNode = new ModelValidationNode(modelBindingKey, bindingContext.ModelMetadata, model)
{ {

View File

@ -21,17 +21,17 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
return null; return null;
} }
var valueProviderResult = await bindingContext.ValueProvider.GetValueAsync(bindingContext.ModelName);
// Check for missing data case 1: There was no <input ... /> element containing this data. // Check for missing data case 1: There was no <input ... /> element containing this data.
var valueProviderResult = await bindingContext.ValueProvider.GetValueAsync(bindingContext.ModelName);
if (valueProviderResult == null) if (valueProviderResult == null)
{ {
return new ModelBindingResult(model: null, key: bindingContext.ModelName, isModelSet: false); return new ModelBindingResult(model: null, key: bindingContext.ModelName, isModelSet: false);
} }
var value = valueProviderResult.AttemptedValue; bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult);
// Check for missing data case 2: There was an <input ... /> element but it was left blank. // Check for missing data case 2: There was an <input ... /> element but it was left blank.
var value = valueProviderResult.AttemptedValue;
if (string.IsNullOrEmpty(value)) if (string.IsNullOrEmpty(value))
{ {
return new ModelBindingResult(model: null, key: bindingContext.ModelName, isModelSet: false); return new ModelBindingResult(model: null, key: bindingContext.ModelName, isModelSet: false);

View File

@ -25,7 +25,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
return null; return null;
} }
object model = null; object model;
var request = bindingContext.OperationBindingContext.HttpContext.Request; var request = bindingContext.OperationBindingContext.HttpContext.Request;
if (request.HasFormContentType) if (request.HasFormContentType)
{ {
@ -36,8 +36,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
} }
else else
{ {
var formValuesLookup = form.ToDictionary(p => p.Key, var formValuesLookup = form.ToDictionary(p => p.Key, p => p.Value);
p => p.Value);
model = new FormCollection(formValuesLookup, form.Files); model = new FormCollection(formValuesLookup, form.Files);
} }
} }

View File

@ -4,7 +4,9 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
#if DNXCORE50
using System.Reflection; using System.Reflection;
#endif
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNet.Http; using Microsoft.AspNet.Http;
using Microsoft.Framework.Internal; using Microsoft.Framework.Internal;
@ -20,33 +22,38 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
/// <inheritdoc /> /// <inheritdoc />
public async Task<ModelBindingResult> BindModelAsync([NotNull] ModelBindingContext bindingContext) public async Task<ModelBindingResult> BindModelAsync([NotNull] ModelBindingContext bindingContext)
{ {
object value;
if (bindingContext.ModelType == typeof(IFormFile)) if (bindingContext.ModelType == typeof(IFormFile))
{ {
var postedFiles = await GetFormFilesAsync(bindingContext); var postedFiles = await GetFormFilesAsync(bindingContext);
var value = postedFiles.FirstOrDefault(); value = postedFiles.FirstOrDefault();
var validationNode =
new ModelValidationNode(bindingContext.ModelName, bindingContext.ModelMetadata, value);
return new ModelBindingResult(
value,
bindingContext.ModelName,
isModelSet: value != null,
validationNode: validationNode);
} }
else if (typeof(IEnumerable<IFormFile>).GetTypeInfo().IsAssignableFrom( else if (typeof(IEnumerable<IFormFile>).IsAssignableFrom(bindingContext.ModelType))
bindingContext.ModelType.GetTypeInfo()))
{ {
var postedFiles = await GetFormFilesAsync(bindingContext); var postedFiles = await GetFormFilesAsync(bindingContext);
var value = ModelBindingHelper.ConvertValuesToCollectionType(bindingContext.ModelType, postedFiles); value = ModelBindingHelper.ConvertValuesToCollectionType(bindingContext.ModelType, postedFiles);
var validationNode = }
new ModelValidationNode(bindingContext.ModelName, bindingContext.ModelMetadata, value); else
return new ModelBindingResult( {
value, // This binder does not support the requested type.
bindingContext.ModelName, return null;
isModelSet: value != null,
validationNode: validationNode);
} }
return null; ModelValidationNode validationNode = null;
if (value != null)
{
validationNode =
new ModelValidationNode(bindingContext.ModelName, bindingContext.ModelMetadata, value);
var valueProviderResult = new ValueProviderResult(rawValue: value);
bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult);
}
return new ModelBindingResult(
value,
bindingContext.ModelName,
isModelSet: value != null,
validationNode: validationNode);
} }
private async Task<List<IFormFile>> GetFormFilesAsync(ModelBindingContext bindingContext) private async Task<List<IFormFile>> GetFormFilesAsync(ModelBindingContext bindingContext)

View File

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
#if DNXCORE50 #if DNXCORE50
using System.Reflection; using System.Reflection;
#endif #endif
@ -59,6 +60,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
bindingContext.ModelName, bindingContext.ModelName,
bindingContext.ModelMetadata, bindingContext.ModelMetadata,
model); model);
var attemptedValue = (model as string) ?? request.Headers.Get(headerName);
var valueProviderResult = new ValueProviderResult(model, attemptedValue, CultureInfo.InvariantCulture);
bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult);
} }
return Task.FromResult( return Task.FromResult(

View File

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using Microsoft.Framework.Internal; using Microsoft.Framework.Internal;
using Xunit; using Xunit;
@ -75,7 +74,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
// Act // Act
var exception = Assert.Throws<InvalidOperationException>(() => source.MarkFieldSkipped("key")); var exception = Assert.Throws<InvalidOperationException>(() => source.MarkFieldSkipped("key"));
// Assert // Assert
Assert.Equal( Assert.Equal(
"A field previously marked invalid should not be marked skipped.", "A field previously marked invalid should not be marked skipped.",
@ -138,7 +137,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
// Act // Act
var exception = Assert.Throws<InvalidOperationException>(() => source.MarkFieldValid("key")); var exception = Assert.Throws<InvalidOperationException>(() => source.MarkFieldValid("key"));
// Assert // Assert
Assert.Equal( Assert.Equal(
"A field previously marked invalid should not be marked valid.", "A field previously marked invalid should not be marked valid.",
@ -278,7 +277,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
// Arrange // Arrange
var validState = new ModelState var validState = new ModelState
{ {
Value = new ValueProviderResult(null, null, null), Value = new ValueProviderResult(rawValue: null),
ValidationState = ModelValidationState.Valid ValidationState = ModelValidationState.Valid
}; };
var msd = new ModelStateDictionary var msd = new ModelStateDictionary
@ -317,7 +316,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
// Arrange // Arrange
var validState = new ModelState var validState = new ModelState
{ {
Value = new ValueProviderResult(null, null, null), Value = new ValueProviderResult(rawValue: null),
ValidationState = ModelValidationState.Valid ValidationState = ModelValidationState.Valid
}; };
var msd = new ModelStateDictionary var msd = new ModelStateDictionary

View File

@ -13,7 +13,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
[Fact] [Fact]
public void ConvertTo_ReturnsNullForReferenceTypes_WhenValueIsNull() public void ConvertTo_ReturnsNullForReferenceTypes_WhenValueIsNull()
{ {
var valueProviderResult = new ValueProviderResult(null, null, CultureInfo.InvariantCulture); var valueProviderResult = new ValueProviderResult(rawValue: null);
var convertedValue = valueProviderResult.ConvertTo(typeof(string)); var convertedValue = valueProviderResult.ConvertTo(typeof(string));
@ -23,7 +23,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
[Fact] [Fact]
public void ConvertTo_ReturnsDefaultForValueTypes_WhenValueIsNull() public void ConvertTo_ReturnsDefaultForValueTypes_WhenValueIsNull()
{ {
var valueProviderResult = new ValueProviderResult(null, null, CultureInfo.InvariantCulture); var valueProviderResult = new ValueProviderResult(rawValue: null);
var convertedValue = valueProviderResult.ConvertTo(typeof(int)); var convertedValue = valueProviderResult.ConvertTo(typeof(int));
@ -34,10 +34,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public void ConvertToCanConvertArraysToSingleElements() public void ConvertToCanConvertArraysToSingleElements()
{ {
// Arrange // Arrange
var vpr = new ValueProviderResult(new int[] { 1, 20, 42 }, "", CultureInfo.InvariantCulture); var valueProviderResult = new ValueProviderResult(
new int[] { 1, 20, 42 },
string.Empty,
CultureInfo.InvariantCulture);
// Act // Act
var converted = (string)vpr.ConvertTo(typeof(string)); var converted = (string)valueProviderResult.ConvertTo(typeof(string));
// Assert // Assert
Assert.Equal("1", converted); Assert.Equal("1", converted);
@ -47,10 +50,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public void ConvertToCanConvertSingleElementsToArrays() public void ConvertToCanConvertSingleElementsToArrays()
{ {
// Arrange // Arrange
var vpr = new ValueProviderResult(42, "", CultureInfo.InvariantCulture); var valueProviderResult = new ValueProviderResult(42, string.Empty, CultureInfo.InvariantCulture);
// Act // Act
var converted = (string[])vpr.ConvertTo(typeof(string[])); var converted = (string[])valueProviderResult.ConvertTo(typeof(string[]));
// Assert // Assert
Assert.NotNull(converted); Assert.NotNull(converted);
@ -62,10 +65,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public void ConvertToCanConvertSingleElementsToSingleElements() public void ConvertToCanConvertSingleElementsToSingleElements()
{ {
// Arrange // Arrange
var vpr = new ValueProviderResult(42, "", CultureInfo.InvariantCulture); var valueProviderResult = new ValueProviderResult(42, string.Empty, CultureInfo.InvariantCulture);
// Act // Act
var converted = (string)vpr.ConvertTo(typeof(string)); var converted = (string)valueProviderResult.ConvertTo(typeof(string));
// Assert // Assert
Assert.NotNull(converted); Assert.NotNull(converted);
@ -77,10 +80,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{ {
// Arrange // Arrange
object original = null; object original = null;
var vpr = new ValueProviderResult(original, "", CultureInfo.InvariantCulture); var valueProviderResult = new ValueProviderResult(original, string.Empty, CultureInfo.InvariantCulture);
// Act // Act
var returned = (int?)vpr.ConvertTo(typeof(int?)); var returned = (int?)valueProviderResult.ConvertTo(typeof(int?));
// Assert // Assert
Assert.Equal(returned, null); Assert.Equal(returned, null);
@ -91,10 +94,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{ {
// Arrange // Arrange
var original = " "; var original = " ";
var vpr = new ValueProviderResult(original, "", CultureInfo.InvariantCulture); var valueProviderResult = new ValueProviderResult(original, string.Empty, CultureInfo.InvariantCulture);
// Act // Act
var returned = (int?)vpr.ConvertTo(typeof(int?)); var returned = (int?)valueProviderResult.ConvertTo(typeof(int?));
// Assert // Assert
Assert.Equal(returned, null); Assert.Equal(returned, null);
@ -104,10 +107,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public void ConvertToReturnsNullIfArrayElementValueIsNull() public void ConvertToReturnsNullIfArrayElementValueIsNull()
{ {
// Arrange // Arrange
var vpr = new ValueProviderResult(new string[] { null }, null, CultureInfo.InvariantCulture); var valueProviderResult = new ValueProviderResult(rawValue: new string[] { null });
// Act // Act
var outValue = vpr.ConvertTo(typeof(int)); var outValue = valueProviderResult.ConvertTo(typeof(int));
// Assert // Assert
Assert.Null(outValue); Assert.Null(outValue);
@ -117,10 +120,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public void ConvertToReturnsNullIfTryingToConvertEmptyArrayToSingleElement() public void ConvertToReturnsNullIfTryingToConvertEmptyArrayToSingleElement()
{ {
// Arrange // Arrange
var vpr = new ValueProviderResult(new int[0], "", CultureInfo.InvariantCulture); var valueProviderResult = new ValueProviderResult(new int[0], string.Empty, CultureInfo.InvariantCulture);
// Act // Act
var outValue = vpr.ConvertTo(typeof(int)); var outValue = valueProviderResult.ConvertTo(typeof(int));
// Assert // Assert
Assert.Null(outValue); Assert.Null(outValue);
@ -132,10 +135,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public void ConvertToReturnsNullIfTrimmedValueIsEmptyString(object value) public void ConvertToReturnsNullIfTrimmedValueIsEmptyString(object value)
{ {
// Arrange // Arrange
var vpr = new ValueProviderResult(value, null, CultureInfo.InvariantCulture); var valueProviderResult = new ValueProviderResult(rawValue: value);
// Act // Act
var outValue = vpr.ConvertTo(typeof(int)); var outValue = valueProviderResult.ConvertTo(typeof(int));
// Assert // Assert
Assert.Null(outValue); Assert.Null(outValue);
@ -145,12 +148,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public void ConvertToReturnsNullIfTrimmedValueIsEmptyString() public void ConvertToReturnsNullIfTrimmedValueIsEmptyString()
{ {
// Arrange // Arrange
var vpr = new ValueProviderResult(rawValue: null, var valueProviderResult = new ValueProviderResult(rawValue: null);
attemptedValue: null,
culture: CultureInfo.InvariantCulture);
// Act // Act
var outValue = vpr.ConvertTo(typeof(int[])); var outValue = valueProviderResult.ConvertTo(typeof(int[]));
// Assert // Assert
Assert.Null(outValue); Assert.Null(outValue);
@ -160,7 +161,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public void ConvertToReturnsValueIfArrayElementIsIntegerAndDestinationTypeIsEnum() public void ConvertToReturnsValueIfArrayElementIsIntegerAndDestinationTypeIsEnum()
{ {
// Arrange // Arrange
var result = new ValueProviderResult(new object[] { 1 }, null, CultureInfo.InvariantCulture); var result = new ValueProviderResult(rawValue: new object[] { 1 });
// Act // Act
var outValue = result.ConvertTo(typeof(IntEnum)); var outValue = result.ConvertTo(typeof(IntEnum));
@ -194,7 +195,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
object expected) object expected)
{ {
// Arrange // Arrange
var result = new ValueProviderResult(new object[] { input }, null, CultureInfo.InvariantCulture); var result = new ValueProviderResult(rawValue: new object[] { input });
// Act // Act
var outValue = result.ConvertTo(enumType); var outValue = result.ConvertTo(enumType);
@ -207,10 +208,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public void ConvertToReturnsValueIfArrayElementIsStringValueAndDestinationTypeIsEnum() public void ConvertToReturnsValueIfArrayElementIsStringValueAndDestinationTypeIsEnum()
{ {
// Arrange // Arrange
var vpr = new ValueProviderResult(new object[] { "1" }, null, CultureInfo.InvariantCulture); var valueProviderResult = new ValueProviderResult(rawValue: new object[] { "1" });
// Act // Act
var outValue = vpr.ConvertTo(typeof(IntEnum)); var outValue = valueProviderResult.ConvertTo(typeof(IntEnum));
// Assert // Assert
Assert.Equal(outValue, IntEnum.Value1); Assert.Equal(outValue, IntEnum.Value1);
@ -220,10 +221,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public void ConvertToReturnsValueIfArrayElementIsStringKeyAndDestinationTypeIsEnum() public void ConvertToReturnsValueIfArrayElementIsStringKeyAndDestinationTypeIsEnum()
{ {
// Arrange // Arrange
var vpr = new ValueProviderResult(new object[] { "Value1" }, null, CultureInfo.InvariantCulture); var valueProviderResult = new ValueProviderResult(rawValue: new object[] { "Value1" });
// Act // Act
var outValue = vpr.ConvertTo(typeof(IntEnum)); var outValue = valueProviderResult.ConvertTo(typeof(IntEnum));
// Assert // Assert
Assert.Equal(outValue, IntEnum.Value1); Assert.Equal(outValue, IntEnum.Value1);
@ -233,10 +234,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public void ConvertToReturnsValueIfElementIsStringAndDestinationIsNullableInteger() public void ConvertToReturnsValueIfElementIsStringAndDestinationIsNullableInteger()
{ {
// Arrange // Arrange
var vpr = new ValueProviderResult("12", null, CultureInfo.InvariantCulture); var valueProviderResult = new ValueProviderResult(rawValue: "12");
// Act // Act
var outValue = vpr.ConvertTo(typeof(int?)); var outValue = valueProviderResult.ConvertTo(typeof(int?));
// Assert // Assert
Assert.Equal(12, outValue); Assert.Equal(12, outValue);
@ -246,10 +247,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public void ConvertToReturnsValueIfElementIsStringAndDestinationIsNullableDouble() public void ConvertToReturnsValueIfElementIsStringAndDestinationIsNullableDouble()
{ {
// Arrange // Arrange
var vpr = new ValueProviderResult("12.5", null, CultureInfo.InvariantCulture); var valueProviderResult = new ValueProviderResult(rawValue: "12.5");
// Act // Act
var outValue = vpr.ConvertTo(typeof(double?)); var outValue = valueProviderResult.ConvertTo(typeof(double?));
// Assert // Assert
Assert.Equal(12.5, outValue); Assert.Equal(12.5, outValue);
@ -259,10 +260,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public void ConvertToReturnsValueIfElementIsDecimalAndDestinationIsNullableInteger() public void ConvertToReturnsValueIfElementIsDecimalAndDestinationIsNullableInteger()
{ {
// Arrange // Arrange
var vpr = new ValueProviderResult(12M, null, CultureInfo.InvariantCulture); var valueProviderResult = new ValueProviderResult(rawValue: 12M);
// Act // Act
var outValue = vpr.ConvertTo(typeof(int?)); var outValue = valueProviderResult.ConvertTo(typeof(int?));
// Assert // Assert
Assert.Equal(12, outValue); Assert.Equal(12, outValue);
@ -272,10 +273,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public void ConvertToReturnsValueIfElementIsDecimalAndDestinationIsNullableDouble() public void ConvertToReturnsValueIfElementIsDecimalAndDestinationIsNullableDouble()
{ {
// Arrange // Arrange
var vpr = new ValueProviderResult(12.5M, null, CultureInfo.InvariantCulture); var valueProviderResult = new ValueProviderResult(rawValue: 12.5M);
// Act // Act
var outValue = vpr.ConvertTo(typeof(double?)); var outValue = valueProviderResult.ConvertTo(typeof(double?));
// Assert // Assert
Assert.Equal(12.5, outValue); Assert.Equal(12.5, outValue);
@ -285,10 +286,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public void ConvertToReturnsValueIfElementIsDecimalDoubleAndDestinationIsNullableInteger() public void ConvertToReturnsValueIfElementIsDecimalDoubleAndDestinationIsNullableInteger()
{ {
// Arrange // Arrange
var vpr = new ValueProviderResult(12.5M, null, CultureInfo.InvariantCulture); var valueProviderResult = new ValueProviderResult(rawValue: 12.5M);
// Act // Act
var outValue = vpr.ConvertTo(typeof(int?)); var outValue = valueProviderResult.ConvertTo(typeof(int?));
// Assert // Assert
Assert.Equal(12, outValue); Assert.Equal(12, outValue);
@ -298,10 +299,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public void ConvertToReturnsValueIfElementIsDecimalDoubleAndDestinationIsNullableLong() public void ConvertToReturnsValueIfElementIsDecimalDoubleAndDestinationIsNullableLong()
{ {
// Arrange // Arrange
var vpr = new ValueProviderResult(12.5M, null, CultureInfo.InvariantCulture); var valueProviderResult = new ValueProviderResult(rawValue: 12.5M);
// Act // Act
var outValue = vpr.ConvertTo(typeof(long?)); var outValue = valueProviderResult.ConvertTo(typeof(long?));
// Assert // Assert
Assert.Equal(12L, outValue); Assert.Equal(12L, outValue);
@ -311,10 +312,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public void ConvertToReturnsValueIfArrayElementInstanceOfDestinationType() public void ConvertToReturnsValueIfArrayElementInstanceOfDestinationType()
{ {
// Arrange // Arrange
var vpr = new ValueProviderResult(new object[] { "some string" }, null, CultureInfo.InvariantCulture); var valueProviderResult = new ValueProviderResult(rawValue: new object[] { "some string" });
// Act // Act
var outValue = vpr.ConvertTo(typeof(string)); var outValue = valueProviderResult.ConvertTo(typeof(string));
// Assert // Assert
Assert.Equal("some string", outValue); Assert.Equal("some string", outValue);
@ -327,10 +328,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public void ConvertTo_ConvertsEnumArrays(object value) public void ConvertTo_ConvertsEnumArrays(object value)
{ {
// Arrange // Arrange
var vpr = new ValueProviderResult(value, null, CultureInfo.InvariantCulture); var valueProviderResult = new ValueProviderResult(rawValue: value);
// Act // Act
var outValue = vpr.ConvertTo(typeof(IntEnum[])); var outValue = valueProviderResult.ConvertTo(typeof(IntEnum[]));
// Assert // Assert
var result = Assert.IsType<IntEnum[]>(outValue); var result = Assert.IsType<IntEnum[]>(outValue);
@ -346,10 +347,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public void ConvertTo_ConvertsFlagsEnumArrays(object value, FlagsEnum[] expected) public void ConvertTo_ConvertsFlagsEnumArrays(object value, FlagsEnum[] expected)
{ {
// Arrange // Arrange
var vpr = new ValueProviderResult(value, null, CultureInfo.InvariantCulture); var valueProviderResult = new ValueProviderResult(rawValue: value);
// Act // Act
var outValue = vpr.ConvertTo(typeof(FlagsEnum[])); var outValue = valueProviderResult.ConvertTo(typeof(FlagsEnum[]));
// Assert // Assert
var result = Assert.IsType<FlagsEnum[]>(outValue); var result = Assert.IsType<FlagsEnum[]>(outValue);
@ -363,10 +364,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{ {
// Arrange // Arrange
var original = new[] { "some string" }; var original = new[] { "some string" };
var vpr = new ValueProviderResult(original, null, CultureInfo.InvariantCulture); var valueProviderResult = new ValueProviderResult(rawValue: original);
// Act // Act
var outValue = vpr.ConvertTo(typeof(string[])); var outValue = valueProviderResult.ConvertTo(typeof(string[]));
// Assert // Assert
Assert.Same(original, outValue); Assert.Same(original, outValue);
@ -379,21 +380,21 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public void ConvertToThrowsIfConverterThrows(Type destinationType) public void ConvertToThrowsIfConverterThrows(Type destinationType)
{ {
// Arrange // Arrange
var vpr = new ValueProviderResult("this-is-not-a-valid-value", null, CultureInfo.InvariantCulture); var valueProviderResult = new ValueProviderResult(rawValue: "this-is-not-a-valid-value");
// Act & Assert // Act & Assert
var ex = Assert.Throws(typeof(FormatException), () => vpr.ConvertTo(destinationType)); var ex = Assert.Throws(typeof(FormatException), () => valueProviderResult.ConvertTo(destinationType));
} }
[Fact] [Fact]
public void ConvertToThrowsIfNoConverterExists() public void ConvertToThrowsIfNoConverterExists()
{ {
// Arrange // Arrange
var vpr = new ValueProviderResult("x", null, CultureInfo.InvariantCulture); var valueProviderResult = new ValueProviderResult(rawValue: "x");
var destinationType = typeof(MyClassWithoutConverter); var destinationType = typeof(MyClassWithoutConverter);
// Act & Assert // Act & Assert
var ex = Assert.Throws<InvalidOperationException>(() => vpr.ConvertTo(destinationType)); var ex = Assert.Throws<InvalidOperationException>(() => valueProviderResult.ConvertTo(destinationType));
Assert.Equal("The parameter conversion from type 'System.String' to type " + Assert.Equal("The parameter conversion from type 'System.String' to type " +
"'Microsoft.AspNet.Mvc.ModelBinding.ValueProviderResultTest+MyClassWithoutConverter' " + "'Microsoft.AspNet.Mvc.ModelBinding.ValueProviderResultTest+MyClassWithoutConverter' " +
"failed because no type converter can convert between these types.", "failed because no type converter can convert between these types.",
@ -405,22 +406,25 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{ {
// Arrange // Arrange
var original = "12,5"; var original = "12,5";
var vpr = new ValueProviderResult(original, null, new CultureInfo("en-GB")); var valueProviderResult = new ValueProviderResult(
rawValue: original,
attemptedValue: null,
culture: new CultureInfo("en-GB"));
var frCulture = new CultureInfo("fr-FR"); var frCulture = new CultureInfo("fr-FR");
// Act // Act
var cultureResult = vpr.ConvertTo(typeof(decimal), frCulture); var cultureResult = valueProviderResult.ConvertTo(typeof(decimal), frCulture);
// Assert // Assert
Assert.Equal(12.5M, cultureResult); Assert.Equal(12.5M, cultureResult);
Assert.Throws<FormatException>(() => vpr.ConvertTo(typeof(decimal))); Assert.Throws<FormatException>(() => valueProviderResult.ConvertTo(typeof(decimal)));
} }
[Fact] [Fact]
public void CulturePropertyDefaultsToInvariantCulture() public void CulturePropertyDefaultsToInvariantCulture()
{ {
// Arrange // Arrange
var result = new ValueProviderResult(null, null, null); var result = new ValueProviderResult(rawValue: null, attemptedValue: null, culture: null);
// Act & assert // Act & assert
Assert.Same(CultureInfo.InvariantCulture, result.Culture); Assert.Same(CultureInfo.InvariantCulture, result.Culture);
@ -431,7 +435,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public void ConvertToCanConvertIntrinsics<T>(object initialValue, T expectedValue) public void ConvertToCanConvertIntrinsics<T>(object initialValue, T expectedValue)
{ {
// Arrange // Arrange
var result = new ValueProviderResult(initialValue, "", CultureInfo.InvariantCulture); var result = new ValueProviderResult(initialValue, string.Empty, CultureInfo.InvariantCulture);
// Act & Assert // Act & Assert
Assert.Equal(expectedValue, result.ConvertTo(typeof(T))); Assert.Equal(expectedValue, result.ConvertTo(typeof(T)));
@ -471,7 +475,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public void ConvertTo_Throws_IfValueIsNotStringData(Type destinationType) public void ConvertTo_Throws_IfValueIsNotStringData(Type destinationType)
{ {
// Arrange // Arrange
var result = new ValueProviderResult(new MyClassWithoutConverter(), "", CultureInfo.InvariantCulture); var result = new ValueProviderResult(
new MyClassWithoutConverter(),
string.Empty,
CultureInfo.InvariantCulture);
// Act // Act
var ex = Assert.Throws<InvalidOperationException>(() => result.ConvertTo(destinationType)); var ex = Assert.Throws<InvalidOperationException>(() => result.ConvertTo(destinationType));
@ -489,7 +496,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
// Arrange // Arrange
var value = "Hello world"; var value = "Hello world";
var destinationType = typeof(MyClassWithoutConverter); var destinationType = typeof(MyClassWithoutConverter);
var result = new ValueProviderResult(value, "", CultureInfo.InvariantCulture); var result = new ValueProviderResult(value, string.Empty, CultureInfo.InvariantCulture);
// Act // Act
var ex = Assert.Throws<InvalidOperationException>(() => result.ConvertTo(destinationType)); var ex = Assert.Throws<InvalidOperationException>(() => result.ConvertTo(destinationType));
@ -513,10 +520,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public void ConvertTo_ConvertsEnumFlags(object value, object expected) public void ConvertTo_ConvertsEnumFlags(object value, object expected)
{ {
// Arrange // Arrange
var vpr = new ValueProviderResult(value, null, CultureInfo.InvariantCulture); var valueProviderResult = new ValueProviderResult(rawValue: value);
// Act // Act
var outValue = (FlagsEnum)vpr.ConvertTo(typeof(FlagsEnum)); var outValue = (FlagsEnum)valueProviderResult.ConvertTo(typeof(FlagsEnum));
// Assert // Assert
Assert.Equal(expected, outValue); Assert.Equal(expected, outValue);

View File

@ -75,7 +75,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
// Assert // Assert
// Returns true because it understands the metadata type. // Returns non-null because it understands the metadata type.
Assert.NotNull(binderResult); Assert.NotNull(binderResult);
Assert.True(binderResult.IsFatalError); Assert.True(binderResult.IsFatalError);
Assert.False(binderResult.IsModelSet); Assert.False(binderResult.IsModelSet);
@ -171,7 +171,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
// Assert // Assert
// Returns true because it understands the metadata type. // Returns non-null because it understands the metadata type.
Assert.NotNull(binderResult); Assert.NotNull(binderResult);
Assert.True(binderResult.IsFatalError); Assert.True(binderResult.IsFatalError);
Assert.False(binderResult.IsModelSet); Assert.False(binderResult.IsModelSet);

View File

@ -34,7 +34,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
Assert.Equal("foo", binderResult.Key); Assert.Equal("foo", binderResult.Key);
Assert.Null(binderResult.Model); Assert.Null(binderResult.Model);
Assert.Empty(bindingContext.ModelState); // No submitted value for "foo". var modelState = Assert.Single(bindingContext.ModelState);
Assert.Equal("foo", modelState.Key);
Assert.NotNull(modelState.Value.Value);
Assert.Equal(value, modelState.Value.Value.RawValue);
} }
[Fact] [Fact]
@ -64,7 +67,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
// Arrange // Arrange
var expected = TestPlatformHelper.IsMono ? var expected = TestPlatformHelper.IsMono ?
"Invalid length." : "Invalid length." :
"The supplied value is invalid for foo."; "The value '\"Fys1\"' is not valid for foo.";
var valueProvider = new SimpleHttpValueProvider() var valueProvider = new SimpleHttpValueProvider()
{ {

View File

@ -490,9 +490,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
var validationContext = testValidationContext.ModelValidationContext; var validationContext = testValidationContext.ModelValidationContext;
// Set the value on model state as a model binder would. // Set the value on model state as a model binder would.
validationContext.ModelState.SetModelValue( var valueProviderResult = new ValueProviderResult(rawValue: "password");
"user.Password", validationContext.ModelState.SetModelValue("user.Password", valueProviderResult);
Mock.Of<ValueProviderResult>());
var validator = new DefaultObjectValidator( var validator = new DefaultObjectValidator(
testValidationContext.ExcludeFilters, testValidationContext.ExcludeFilters,
testValidationContext.ModelMetadataProvider); testValidationContext.ModelMetadataProvider);
@ -513,6 +512,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
Assert.Equal("user.Password", entry.Key); Assert.Equal("user.Password", entry.Key);
Assert.Empty(entry.Value.Errors); Assert.Empty(entry.Value.Errors);
Assert.Equal(entry.Value.ValidationState, ModelValidationState.Skipped); Assert.Equal(entry.Value.ValidationState, ModelValidationState.Skipped);
Assert.Same(valueProviderResult, entry.Value.Value);
} }
private class Person2 private class Person2
@ -536,7 +536,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
validationContext.ModelState.Add("person.Address", new ModelState()); validationContext.ModelState.Add("person.Address", new ModelState());
var validator = new DefaultObjectValidator( var validator = new DefaultObjectValidator(
testValidationContext.ExcludeFilters, testValidationContext.ExcludeFilters,
testValidationContext.ModelMetadataProvider); testValidationContext.ModelMetadataProvider);
var modelExplorer = testValidationContext.ModelValidationContext.ModelExplorer; var modelExplorer = testValidationContext.ModelValidationContext.ModelExplorer;
var topLevelValidationNode = new ModelValidationNode( var topLevelValidationNode = new ModelValidationNode(

View File

@ -166,10 +166,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
viewContext.ViewData[nameof(Model.Name)] = "ignored ViewData value"; viewContext.ViewData[nameof(Model.Name)] = "ignored ViewData value";
var valueProviderResult = new ValueProviderResult( var valueProviderResult = new ValueProviderResult(rawValue);
rawValue,
attemptedValue: null,
culture: CultureInfo.InvariantCulture);
viewContext.ModelState.SetModelValue(nameof(Model.Name), valueProviderResult); viewContext.ModelState.SetModelValue(nameof(Model.Name), valueProviderResult);
// Act // Act
@ -201,10 +198,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(string), "ignored model value"); var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(string), "ignored model value");
var valueProviderResult = new ValueProviderResult( var valueProviderResult = new ValueProviderResult(rawValue);
rawValue,
attemptedValue: null,
culture: CultureInfo.InvariantCulture);
viewContext.ModelState.SetModelValue(nameof(Model.Name), valueProviderResult); viewContext.ModelState.SetModelValue(nameof(Model.Name), valueProviderResult);
// Act // Act
@ -254,10 +248,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(string), rawValue); var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(string), rawValue);
var valueProviderResult = new ValueProviderResult( var valueProviderResult = new ValueProviderResult(rawValue: null);
rawValue: null,
attemptedValue: null,
culture: CultureInfo.InvariantCulture);
viewContext.ModelState.SetModelValue(nameof(Model.Name), valueProviderResult); viewContext.ModelState.SetModelValue(nameof(Model.Name), valueProviderResult);
// Act // Act
@ -285,10 +276,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
var viewContext = GetViewContext<Model>(model, metadataProvider); var viewContext = GetViewContext<Model>(model, metadataProvider);
viewContext.ViewData[nameof(Model.Name)] = rawValue; viewContext.ViewData[nameof(Model.Name)] = rawValue;
var valueProviderResult = new ValueProviderResult( var valueProviderResult = new ValueProviderResult(rawValue: null);
rawValue: null,
attemptedValue: null,
culture: CultureInfo.InvariantCulture);
viewContext.ModelState.SetModelValue(nameof(Model.Name), valueProviderResult); viewContext.ModelState.SetModelValue(nameof(Model.Name), valueProviderResult);
// Act // Act
@ -313,10 +301,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
var viewContext = GetViewContext<Model>(model, metadataProvider); var viewContext = GetViewContext<Model>(model, metadataProvider);
var valueProviderResult = new ValueProviderResult( var valueProviderResult = new ValueProviderResult(rawValue: null);
rawValue: null,
attemptedValue: null,
culture: CultureInfo.InvariantCulture);
viewContext.ModelState.SetModelValue(nameof(Model.Name), valueProviderResult); viewContext.ModelState.SetModelValue(nameof(Model.Name), valueProviderResult);
// Act // Act
@ -376,10 +361,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
var modelExplorer = var modelExplorer =
metadataProvider.GetModelExplorerForType(typeof(List<string>), new List<string>(rawValue)); metadataProvider.GetModelExplorerForType(typeof(List<string>), new List<string>(rawValue));
var valueProviderResult = new ValueProviderResult( var valueProviderResult = new ValueProviderResult(rawValue: null);
rawValue: null,
attemptedValue: null,
culture: CultureInfo.InvariantCulture);
viewContext.ModelState.SetModelValue(nameof(Model.Collection), valueProviderResult); viewContext.ModelState.SetModelValue(nameof(Model.Collection), valueProviderResult);
// Act // Act
@ -407,10 +389,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
var viewContext = GetViewContext<Model>(model, metadataProvider); var viewContext = GetViewContext<Model>(model, metadataProvider);
viewContext.ViewData[nameof(Model.Collection)] = rawValue; viewContext.ViewData[nameof(Model.Collection)] = rawValue;
var valueProviderResult = new ValueProviderResult( var valueProviderResult = new ValueProviderResult(rawValue: null);
rawValue: null,
attemptedValue: null,
culture: CultureInfo.InvariantCulture);
viewContext.ModelState.SetModelValue(nameof(Model.Collection), valueProviderResult); viewContext.ModelState.SetModelValue(nameof(Model.Collection), valueProviderResult);
// Act // Act
@ -438,10 +417,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
var viewContext = GetViewContext<Model>(model, metadataProvider); var viewContext = GetViewContext<Model>(model, metadataProvider);
var valueProviderResult = new ValueProviderResult( var valueProviderResult = new ValueProviderResult(rawValue: null);
rawValue: null,
attemptedValue: null,
culture: CultureInfo.InvariantCulture);
viewContext.ModelState.SetModelValue(nameof(Model.Collection), valueProviderResult); viewContext.ModelState.SetModelValue(nameof(Model.Collection), valueProviderResult);
// Act // Act
@ -552,10 +528,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
var htmlGenerator = GetGenerator(metadataProvider); var htmlGenerator = GetGenerator(metadataProvider);
var viewContext = GetViewContext<Model>(model: null, metadataProvider: metadataProvider); var viewContext = GetViewContext<Model>(model: null, metadataProvider: metadataProvider);
var valueProviderResult = new ValueProviderResult( var valueProviderResult = new ValueProviderResult(rawValue);
rawValue,
attemptedValue: null,
culture: CultureInfo.InvariantCulture);
viewContext.ModelState.SetModelValue(propertyName, valueProviderResult); viewContext.ModelState.SetModelValue(propertyName, valueProviderResult);
// Act // Act

View File

@ -3,12 +3,9 @@
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.IO; using System.IO;
using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Mvc.ModelBinding; using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.Framework.DependencyInjection;
using Xunit; using Xunit;
namespace Microsoft.AspNet.Mvc.IntegrationTests namespace Microsoft.AspNet.Mvc.IntegrationTests
@ -71,10 +68,10 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
{ {
// Arrange // Arrange
var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); var argumentBinder = ModelBindingTestHelper.GetArgumentBinder();
var parameter = new ParameterDescriptor() var parameter = new ParameterDescriptor
{ {
Name = "Parameter1", Name = "Parameter1",
BindingInfo = new BindingInfo() BindingInfo = new BindingInfo
{ {
BinderModelName = "CustomParameter", BinderModelName = "CustomParameter",
BindingSource = BindingSource.Body BindingSource = BindingSource.Body
@ -99,8 +96,11 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
Assert.NotNull(modelBindingResult); Assert.NotNull(modelBindingResult);
Assert.True(modelBindingResult.IsModelSet); Assert.True(modelBindingResult.IsModelSet);
Assert.Null(modelBindingResult.Model); Assert.Null(modelBindingResult.Model);
Assert.Empty(modelState.Keys);
Assert.True(modelState.IsValid); Assert.True(modelState.IsValid);
var entry = Assert.Single(modelState);
Assert.Empty(entry.Key);
Assert.Null(entry.Value.Value.RawValue);
} }
private class Person4 private class Person4
@ -111,14 +111,14 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
} }
[Fact] [Fact]
public async Task FromBodyAndRequiredOnValueTypeProperty_EmptyBody_AddsModelStateError() public async Task FromBodyAndRequiredOnValueTypeProperty_EmptyBody_JsonFormatterAddsModelStateError()
{ {
// Arrange // Arrange
var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); var argumentBinder = ModelBindingTestHelper.GetArgumentBinder();
var parameter = new ParameterDescriptor() var parameter = new ParameterDescriptor
{ {
Name = "Parameter1", Name = "Parameter1",
BindingInfo = new BindingInfo() BindingInfo = new BindingInfo
{ {
BinderModelName = "CustomParameter", BinderModelName = "CustomParameter",
}, },
@ -141,16 +141,16 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
Assert.NotNull(modelBindingResult); Assert.NotNull(modelBindingResult);
Assert.True(modelBindingResult.IsModelSet); Assert.True(modelBindingResult.IsModelSet);
var boundPerson = Assert.IsType<Person4>(modelBindingResult.Model); var boundPerson = Assert.IsType<Person4>(modelBindingResult.Model);
Assert.NotNull(boundPerson);
Assert.False(modelState.IsValid);
// The error with an empty key is a bug(#2416) in our implementation which does not append the prefix and Assert.False(modelState.IsValid);
// use that along with the path. The expected key here would be CustomParameter.Address. var entry = Assert.Single(modelState);
var key = Assert.Single(modelState.Keys, k => k == ""); Assert.Equal(string.Empty, entry.Key);
var error = Assert.Single(modelState[""].Errors); var error = Assert.Single(entry.Value.Errors);
Assert.StartsWith( Assert.NotNull(error.Exception);
"No JSON content found and type 'System.Int32' is not nullable.",
error.Exception.Message); // Json.NET currently throws an exception starting with "No JSON content found and type 'System.Int32' is
// not nullable." but do not tie test to a particular Json.NET build.
Assert.NotEmpty(error.Exception.Message);
} }
private class Person2 private class Person2
@ -167,16 +167,16 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
public int Zip { get; set; } public int Zip { get; set; }
} }
[Theory(Skip = "There should be entries for all model properties which are bound. #2445")] [Theory]
[InlineData("{ \"Zip\" : 123 }")] [InlineData("{ \"Zip\" : 123 }")]
[InlineData("{}")] [InlineData("{}")]
public async Task FromBodyOnTopLevelProperty_RequiredOnSubProperty_AddsModelStateError(string inputText) public async Task FromBodyOnTopLevelProperty_RequiredOnSubProperty_AddsModelStateError(string inputText)
{ {
// Arrange // Arrange
var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); var argumentBinder = ModelBindingTestHelper.GetArgumentBinder();
var parameter = new ParameterDescriptor() var parameter = new ParameterDescriptor
{ {
BindingInfo = new BindingInfo() BindingInfo = new BindingInfo
{ {
BinderModelName = "CustomParameter", BinderModelName = "CustomParameter",
}, },
@ -200,14 +200,15 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
Assert.True(modelBindingResult.IsModelSet); Assert.True(modelBindingResult.IsModelSet);
var boundPerson = Assert.IsType<Person2>(modelBindingResult.Model); var boundPerson = Assert.IsType<Person2>(modelBindingResult.Model);
Assert.NotNull(boundPerson); Assert.NotNull(boundPerson);
Assert.False(modelState.IsValid); Assert.False(modelState.IsValid);
Assert.Equal(2, modelState.Keys.Count); Assert.Equal(2, modelState.Keys.Count);
var zip = Assert.Single(modelState.Keys, k => k == "CustomParameter.Address.Zip"); var address = Assert.Single(modelState, kvp => kvp.Key == "CustomParameter.Address").Value;
Assert.Equal(ModelValidationState.Valid, modelState[zip].ValidationState); Assert.Equal(ModelValidationState.Unvalidated, address.ValidationState);
var street = Assert.Single(modelState.Keys, k => k == "CustomParameter.Address.Street"); var street = Assert.Single(modelState, kvp => kvp.Key == "CustomParameter.Address.Street").Value;
Assert.Equal(ModelValidationState.Invalid, modelState[street].ValidationState); Assert.Equal(ModelValidationState.Invalid, street.ValidationState);
var error = Assert.Single(modelState[street].Errors); var error = Assert.Single(street.Errors);
Assert.Equal("The Street field is required.", error.ErrorMessage); Assert.Equal("The Street field is required.", error.ErrorMessage);
} }
@ -225,16 +226,16 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
public int Zip { get; set; } public int Zip { get; set; }
} }
[Theory(Skip = "There should be entries for all model properties which are bound. #2445")] [Theory]
[InlineData("{ \"Street\" : \"someStreet\" }")] [InlineData("{ \"Street\" : \"someStreet\" }")]
[InlineData("{}")] [InlineData("{}")]
public async Task FromBodyOnProperty_RequiredOnValueTypeSubProperty_AddsModelStateError(string inputText) public async Task FromBodyOnProperty_Succeeds_IgnoresRequiredOnValueTypeSubProperty(string inputText)
{ {
// Arrange // Arrange
var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); var argumentBinder = ModelBindingTestHelper.GetArgumentBinder();
var parameter = new ParameterDescriptor() var parameter = new ParameterDescriptor
{ {
BindingInfo = new BindingInfo() BindingInfo = new BindingInfo
{ {
BinderModelName = "CustomParameter", BinderModelName = "CustomParameter",
}, },
@ -255,20 +256,11 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
// Assert // Assert
Assert.NotNull(modelBindingResult); Assert.NotNull(modelBindingResult);
Assert.True(modelBindingResult.IsModelSet); Assert.True(modelBindingResult.IsModelSet);
var boundPerson = Assert.IsType<Person3>(modelBindingResult.Model); Assert.IsType<Person3>(modelBindingResult.Model);
Assert.NotNull(boundPerson);
Assert.False(modelState.IsValid);
var street = Assert.Single(modelState.Keys, k => k == "CustomParameter.Address.Street");
Assert.Equal(ModelValidationState.Valid, modelState[street].ValidationState);
// The error with an empty key is a bug(#2416) in our implementation which does not append the prefix and Assert.True(modelState.IsValid);
// use that along with the path. The expected key here would be Address. var entry = Assert.Single(modelState);
var zip = Assert.Single(modelState.Keys, k => k == "CustomParameter.Address.Zip"); Assert.Equal("CustomParameter.Address", entry.Key);
Assert.Equal(ModelValidationState.Valid, modelState[zip].ValidationState);
var error = Assert.Single(modelState[""].Errors);
Assert.StartsWith(
"Required property 'Zip' not found in JSON. Path ''",
error.Exception.Message);
} }
} }
} }

View File

@ -16,7 +16,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
public byte[] Token { get; set; } public byte[] Token { get; set; }
} }
[Theory(Skip = "ModelState.Value not set due to #2445")] [Theory]
[InlineData(true)] [InlineData(true)]
[InlineData(false)] [InlineData(false)]
public async Task BindProperty_WithData_GetsBound(bool fallBackScenario) public async Task BindProperty_WithData_GetsBound(bool fallBackScenario)
@ -61,19 +61,12 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
// ModelState // ModelState
Assert.True(modelState.IsValid); Assert.True(modelState.IsValid);
Assert.Equal(2, modelState.Keys.Count); var entry = Assert.Single(modelState);
Assert.Single(modelState.Keys, k => k == prefix); Assert.Equal(queryStringKey, entry.Key);
Assert.Single(modelState.Keys, k => k == queryStringKey); Assert.Empty(entry.Value.Errors);
Assert.Equal(ModelValidationState.Valid, entry.Value.ValidationState);
var key = Assert.Single(modelState.Keys, k => k == queryStringKey + "[0]"); Assert.Equal(value, entry.Value.Value.AttemptedValue);
Assert.NotNull(modelState[key].Value); // should be non null bug #2445. Assert.Equal(value, entry.Value.Value.RawValue);
Assert.Empty(modelState[key].Errors);
Assert.Equal(ModelValidationState.Valid, modelState[key].ValidationState);
key = Assert.Single(modelState.Keys, k => k == queryStringKey + "[1]");
Assert.NotNull(modelState[key].Value); // should be non null bug #2445.
Assert.Empty(modelState[key].Errors);
Assert.Equal(ModelValidationState.Valid, modelState[key].ValidationState);
} }
[Fact] [Fact]
@ -109,19 +102,18 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
Assert.Empty(modelState.Keys); Assert.Empty(modelState.Keys);
} }
[Fact(Skip = "ModelState.Value not set due to #2445")] [Fact]
public async Task BindParameter_WithData_GetsBound() public async Task BindParameter_WithData_GetsBound()
{ {
// Arrange // Arrange
var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); var argumentBinder = ModelBindingTestHelper.GetArgumentBinder();
var parameter = new ParameterDescriptor() var parameter = new ParameterDescriptor
{ {
Name = "Parameter1", Name = "Parameter1",
BindingInfo = new BindingInfo() BindingInfo = new BindingInfo
{ {
BinderModelName = "CustomParameter", BinderModelName = "CustomParameter",
}, },
ParameterType = typeof(byte[]) ParameterType = typeof(byte[])
}; };
@ -151,16 +143,11 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
// ModelState // ModelState
Assert.True(modelState.IsValid); Assert.True(modelState.IsValid);
var entry = Assert.Single(modelState);
Assert.Equal(3, modelState.Count); Assert.Equal("CustomParameter", entry.Key);
Assert.Single(modelState.Keys, k => k == "CustomParameter[0]"); Assert.Empty(entry.Value.Errors);
Assert.Single(modelState.Keys, k => k == "CustomParameter[1]"); Assert.Equal(value, entry.Value.Value.AttemptedValue);
var key = Assert.Single(modelState.Keys, k => k == "CustomParameter[2]"); Assert.Equal(value, entry.Value.Value.RawValue);
Assert.NotNull(modelState[key].Value);
Assert.Equal(value, modelState[key].Value.AttemptedValue);
Assert.Equal(expectedValue, modelState[key].Value.RawValue);
Assert.Empty(modelState[key].Errors);
} }
} }
} }

View File

@ -27,12 +27,12 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
public FormCollection FileCollection { get; set; } public FormCollection FileCollection { get; set; }
} }
[Fact(Skip = "ModelState.Value not set due to #2445")] [Fact]
public async Task BindProperty_WithData_WithEmptyPrefix_GetsBound() public async Task BindProperty_WithData_WithEmptyPrefix_GetsBound()
{ {
// Arrange // Arrange
var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); var argumentBinder = ModelBindingTestHelper.GetArgumentBinder();
var parameter = new ParameterDescriptor() var parameter = new ParameterDescriptor
{ {
Name = "Parameter1", Name = "Parameter1",
BindingInfo = new BindingInfo(), BindingInfo = new BindingInfo(),
@ -69,12 +69,10 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
// ModelState // ModelState
Assert.True(modelState.IsValid); Assert.True(modelState.IsValid);
Assert.Equal(2, modelState.Count); var entry = Assert.Single(modelState);
Assert.Single(modelState.Keys, k => k == "Address.Zip"); Assert.Equal("Address.Zip", entry.Key);
var key = Assert.Single(modelState.Keys, k => k == "Address.File"); Assert.Empty(entry.Value.Errors);
Assert.NotNull(modelState[key].Value); // should be non null bug #2445. Assert.Equal(ModelValidationState.Valid, entry.Value.ValidationState);
Assert.Empty(modelState[key].Errors);
Assert.Equal(ModelValidationState.Valid, modelState[key].ValidationState);
} }
[Fact] [Fact]
@ -82,15 +80,14 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
{ {
// Arrange // Arrange
var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); var argumentBinder = ModelBindingTestHelper.GetArgumentBinder();
var parameter = new ParameterDescriptor() var parameter = new ParameterDescriptor
{ {
Name = "Parameter1", Name = "Parameter1",
BindingInfo = new BindingInfo() BindingInfo = new BindingInfo
{ {
// Setting a custom parameter prevents it from falling back to an empty prefix. // Setting a custom parameter prevents it from falling back to an empty prefix.
BinderModelName = "CustomParameter", BinderModelName = "CustomParameter",
}, },
ParameterType = typeof(FormCollection) ParameterType = typeof(FormCollection)
}; };
@ -113,7 +110,6 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
// Model // Model
var formCollection = Assert.IsType<FormCollection>(modelBindingResult.Model); var formCollection = Assert.IsType<FormCollection>(modelBindingResult.Model);
Assert.NotNull(formCollection);
var file = Assert.Single(formCollection.Files); var file = Assert.Single(formCollection.Files);
Assert.NotNull(file); Assert.NotNull(file);
Assert.Equal("form-data; name=CustomParameter; filename=text.txt", file.ContentDisposition); Assert.Equal("form-data; name=CustomParameter; filename=text.txt", file.ContentDisposition);
@ -122,10 +118,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
// ModelState // ModelState
Assert.True(modelState.IsValid); Assert.True(modelState.IsValid);
Assert.Empty(modelState);
// Validation should be skipped because we do not validate any parameters and since IFormFile is not
// IValidatableObject, we should have no entries in the model state dictionary.
Assert.Empty(modelState.Keys);
} }
[Fact] [Fact]
@ -133,21 +126,18 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
{ {
// Arrange // Arrange
var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); var argumentBinder = ModelBindingTestHelper.GetArgumentBinder();
var parameter = new ParameterDescriptor() var parameter = new ParameterDescriptor
{ {
Name = "Parameter1", Name = "Parameter1",
BindingInfo = new BindingInfo() BindingInfo = new BindingInfo
{ {
BinderModelName = "CustomParameter", BinderModelName = "CustomParameter",
}, },
ParameterType = typeof(FormCollection) ParameterType = typeof(FormCollection)
}; };
// No data is passed. // No data is passed.
var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request => var operationContext = ModelBindingTestHelper.GetOperationBindingContext();
{
});
var modelState = new ModelStateDictionary(); var modelState = new ModelStateDictionary();
@ -162,7 +152,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
// ModelState // ModelState
Assert.True(modelState.IsValid); Assert.True(modelState.IsValid);
Assert.Empty(modelState.Keys); Assert.Empty(modelState);
// FormCollection // FormCollection
Assert.Empty(collection); Assert.Empty(collection);

View File

@ -27,7 +27,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
public IFormFile File { get; set; } public IFormFile File { get; set; }
} }
[Fact(Skip = "ModelState.Value not set due to #2445")] [Fact]
public async Task BindProperty_WithData_WithEmptyPrefix_GetsBound() public async Task BindProperty_WithData_WithEmptyPrefix_GetsBound()
{ {
// Arrange // Arrange
@ -71,7 +71,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
Assert.Equal(2, modelState.Count); Assert.Equal(2, modelState.Count);
Assert.Single(modelState.Keys, k => k == "Address.Zip"); Assert.Single(modelState.Keys, k => k == "Address.Zip");
var key = Assert.Single(modelState.Keys, k => k == "Address.File"); var key = Assert.Single(modelState.Keys, k => k == "Address.File");
Assert.NotNull(modelState[key].Value); // should be non null bug #2445. Assert.NotNull(modelState[key].Value);
Assert.Empty(modelState[key].Errors); Assert.Empty(modelState[key].Errors);
Assert.Equal(ModelValidationState.Valid, modelState[key].ValidationState); Assert.Equal(ModelValidationState.Valid, modelState[key].ValidationState);
} }
@ -81,15 +81,14 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
{ {
// Arrange // Arrange
var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); var argumentBinder = ModelBindingTestHelper.GetArgumentBinder();
var parameter = new ParameterDescriptor() var parameter = new ParameterDescriptor
{ {
Name = "Parameter1", Name = "Parameter1",
BindingInfo = new BindingInfo() BindingInfo = new BindingInfo
{ {
// Setting a custom parameter prevents it from falling back to an empty prefix. // Setting a custom parameter prevents it from falling back to an empty prefix.
BinderModelName = "CustomParameter", BinderModelName = "CustomParameter",
}, },
ParameterType = typeof(IFormFile) ParameterType = typeof(IFormFile)
}; };
@ -119,10 +118,12 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
// ModelState // ModelState
Assert.True(modelState.IsValid); Assert.True(modelState.IsValid);
var entry = Assert.Single(modelState);
// Validation should be skipped because we do not validate any parameters and since IFormFile is not Assert.Equal("CustomParameter", entry.Key);
// IValidatableObject, we should have no entries in the model state dictionary. Assert.Empty(entry.Value.Errors);
Assert.Empty(modelState.Keys); Assert.Equal(ModelValidationState.Valid, entry.Value.ValidationState);
Assert.Null(entry.Value.Value.AttemptedValue);
Assert.Equal(file, entry.Value.Value.RawValue);
} }
[Fact] [Fact]

View File

@ -18,8 +18,8 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
// This isn't an especially useful scenario - but it exercises what happens when you // This isn't an especially useful scenario - but it exercises what happens when you
// try to use a Collection of something that is bound greedily by model-type. // try to use a Collection of something that is bound greedily by model-type.
// //
// In this example we choose IFormCollection - because IFormCollection has a dedicated // In this example we choose IFormCollection because IFormCollection has a dedicated
// model binder. // model binder.
[Fact] [Fact]
public async Task GenericModelBinder_BindsCollection_ElementTypeFromGreedyModelBinder_WithPrefix_Success() public async Task GenericModelBinder_BindsCollection_ElementTypeFromGreedyModelBinder_WithPrefix_Success()
{ {
@ -47,12 +47,12 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
Assert.True(modelBindingResult.IsModelSet); Assert.True(modelBindingResult.IsModelSet);
var model = Assert.IsType<List<IFormCollection>>(modelBindingResult.Model); var model = Assert.IsType<List<IFormCollection>>(modelBindingResult.Model);
Assert.Equal(1, model.Count); var formCollection = Assert.Single(model);
Assert.NotNull(model[0]); Assert.NotNull(formCollection);
Assert.Equal(0, modelState.Count);
Assert.Equal(0, modelState.ErrorCount); Assert.Equal(0, modelState.ErrorCount);
Assert.True(modelState.IsValid); Assert.True(modelState.IsValid);
Assert.Empty(modelState);
} }
// This isn't an especially useful scenario - but it exercises what happens when you // This isn't an especially useful scenario - but it exercises what happens when you
@ -86,12 +86,12 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
Assert.True(modelBindingResult.IsModelSet); Assert.True(modelBindingResult.IsModelSet);
var model = Assert.IsType<List<IFormCollection>>(modelBindingResult.Model); var model = Assert.IsType<List<IFormCollection>>(modelBindingResult.Model);
Assert.Equal(1, model.Count); var formCollection = Assert.Single(model);
Assert.NotNull(model[0]); Assert.NotNull(formCollection);
Assert.Equal(0, modelState.Count);
Assert.Equal(0, modelState.ErrorCount); Assert.Equal(0, modelState.ErrorCount);
Assert.True(modelState.IsValid); Assert.True(modelState.IsValid);
Assert.Empty(modelState);
} }
// This isn't an especially useful scenario - but it exercises what happens when you // This isn't an especially useful scenario - but it exercises what happens when you
@ -217,7 +217,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
ParameterType = typeof(Dictionary<string, int>[]) ParameterType = typeof(Dictionary<string, int>[])
}; };
var operationContext = ModelBindingTestHelper.GetOperationBindingContext((request) => var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request =>
{ {
request.QueryString = new QueryString("?parameter[0][0].Key=key0&parameter[0][0].Value=10"); request.QueryString = new QueryString("?parameter[0][0].Key=key0&parameter[0][0].Value=10");
}); });
@ -263,7 +263,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
ParameterType = typeof(Dictionary<string, int>[]) ParameterType = typeof(Dictionary<string, int>[])
}; };
var operationContext = ModelBindingTestHelper.GetOperationBindingContext((request) => var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request =>
{ {
request.QueryString = new QueryString("?[0][0].Key=key0&[0][0].Value=10"); request.QueryString = new QueryString("?[0][0].Key=key0&[0][0].Value=10");
}); });
@ -309,7 +309,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
ParameterType = typeof(Dictionary<string, int>[]) ParameterType = typeof(Dictionary<string, int>[])
}; };
var operationContext = ModelBindingTestHelper.GetOperationBindingContext((request) => var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request =>
{ {
request.QueryString = new QueryString("?"); request.QueryString = new QueryString("?");
}); });
@ -345,7 +345,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
ParameterType = typeof(ICollection<KeyValuePair<string, int>>) ParameterType = typeof(ICollection<KeyValuePair<string, int>>)
}; };
var operationContext = ModelBindingTestHelper.GetOperationBindingContext((request) => var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request =>
{ {
request.QueryString = new QueryString("?parameter[0].Key=key0&parameter[0].Value=10"); request.QueryString = new QueryString("?parameter[0].Key=key0&parameter[0].Value=10");
}); });
@ -390,7 +390,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
ParameterType = typeof(ICollection<KeyValuePair<string, int>>) ParameterType = typeof(ICollection<KeyValuePair<string, int>>)
}; };
var operationContext = ModelBindingTestHelper.GetOperationBindingContext((request) => var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request =>
{ {
request.QueryString = new QueryString("?[0].Key=key0&[0].Value=10"); request.QueryString = new QueryString("?[0].Key=key0&[0].Value=10");
}); });
@ -435,7 +435,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
ParameterType = typeof(Collection<KeyValuePair<string, int>>) ParameterType = typeof(Collection<KeyValuePair<string, int>>)
}; };
var operationContext = ModelBindingTestHelper.GetOperationBindingContext((request) => var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request =>
{ {
request.QueryString = new QueryString("?"); request.QueryString = new QueryString("?");
}); });
@ -471,7 +471,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
ParameterType = typeof(Dictionary<string, List<int>>) ParameterType = typeof(Dictionary<string, List<int>>)
}; };
var operationContext = ModelBindingTestHelper.GetOperationBindingContext((request) => var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request =>
{ {
request.QueryString = new QueryString( request.QueryString = new QueryString(
"?parameter[0].Key=key0&parameter[0].Value[0]=10&parameter[0].Value[1]=11"); "?parameter[0].Key=key0&parameter[0].Value[0]=10&parameter[0].Value[1]=11");
@ -521,7 +521,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
ParameterType = typeof(Dictionary<string, List<int>>) ParameterType = typeof(Dictionary<string, List<int>>)
}; };
var operationContext = ModelBindingTestHelper.GetOperationBindingContext((request) => var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request =>
{ {
request.QueryString = new QueryString("?[0].Key=key0&[0].Value[0]=10&[0].Value[1]=11"); request.QueryString = new QueryString("?[0].Key=key0&[0].Value[0]=10&[0].Value[1]=11");
}); });
@ -570,7 +570,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
ParameterType = typeof(Dictionary<string, List<int>>) ParameterType = typeof(Dictionary<string, List<int>>)
}; };
var operationContext = ModelBindingTestHelper.GetOperationBindingContext((request) => var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request =>
{ {
request.QueryString = new QueryString("?"); request.QueryString = new QueryString("?");
}); });

View File

@ -65,7 +65,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
Assert.Equal("The Street field is required.", error.ErrorMessage); Assert.Equal("The Street field is required.", error.ErrorMessage);
} }
[Fact(Skip = "ModelState.Value not set due to #2445")] [Fact]
public async Task BindPropertyFromHeader_WithPrefix_GetsBound() public async Task BindPropertyFromHeader_WithPrefix_GetsBound()
{ {
// Arrange // Arrange
@ -104,19 +104,17 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
// ModelState // ModelState
Assert.True(modelState.IsValid); Assert.True(modelState.IsValid);
Assert.Equal(3, modelState.Count); var entry = Assert.Single(modelState);
Assert.Single(modelState.Keys, k => k == "prefix.Address"); Assert.Equal("prefix.Address.Header", entry.Key);
Assert.Single(modelState.Keys, k => k == "prefix"); Assert.Empty(entry.Value.Errors);
Assert.Equal(ModelValidationState.Valid, entry.Value.ValidationState);
var key = Assert.Single(modelState.Keys, k => k == "prefix.Address.Header"); Assert.Equal("someValue", entry.Value.Value.AttemptedValue);
Assert.NotNull(modelState[key].Value); Assert.Equal("someValue", entry.Value.Value.RawValue);
Assert.Equal("someValue", modelState[key].Value.RawValue);
Assert.Equal("someValue", modelState[key].Value.AttemptedValue);
} }
// The scenario is interesting as we to bind the top level model we fallback to empty prefix, // The scenario is interesting as we to bind the top level model we fallback to empty prefix,
// and hence the model state keys have an empty prefix. // and hence the model state keys have an empty prefix.
[Fact(Skip = "ModelState.Value not set due to #2445.")] [Fact]
public async Task BindPropertyFromHeader_WithData_WithEmptyPrefix_GetsBound() public async Task BindPropertyFromHeader_WithData_WithEmptyPrefix_GetsBound()
{ {
// Arrange // Arrange
@ -128,11 +126,8 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
ParameterType = typeof(Person) ParameterType = typeof(Person)
}; };
// Do not add any headers. var operationContext = ModelBindingTestHelper.GetOperationBindingContext(
var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request => { request => request.Headers.Add("Header", new[] { "someValue" }));
request.Headers.Add("Header", new[] { "someValue" });
});
var modelState = new ModelStateDictionary(); var modelState = new ModelStateDictionary();
// Act // Act
@ -152,27 +147,29 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
// ModelState // ModelState
Assert.True(modelState.IsValid); Assert.True(modelState.IsValid);
Assert.Equal(2, modelState.Count); var entry = Assert.Single(modelState);
Assert.Single(modelState.Keys, k => k == "Address"); Assert.Equal("Address.Header", entry.Key);
var key = Assert.Single(modelState.Keys, k => k == "Address.Header"); Assert.Empty(entry.Value.Errors);
Assert.NotNull(modelState[key].Value); Assert.Equal(ModelValidationState.Valid, entry.Value.ValidationState);
Assert.Equal("someValue", modelState[key].Value.RawValue); Assert.Equal("someValue", entry.Value.Value.AttemptedValue);
Assert.Equal("someValue", modelState[key].Value.AttemptedValue); Assert.Equal("someValue", entry.Value.Value.RawValue);
} }
[Theory(Skip = "Greedy Model Binders should add a value in model state #2445.")] [Theory]
[InlineData(typeof(string[]), "value1, value2, value3")] [InlineData(typeof(string[]), "value1, value2, value3")]
[InlineData(typeof(string), "value")] [InlineData(typeof(string), "value")]
public async Task BindParameterFromHeader_WithData_WithPrefix_ModelGetsBound(Type modelType, string value) public async Task BindParameterFromHeader_WithData_WithPrefix_ModelGetsBound(Type modelType, string value)
{ {
// Arrange // Arrange
var expectedValue = value.Split(',').Select(v => v.Trim()); var expectedValue = modelType == typeof(string) ?
(object)value :
(object)value.Split(',').Select(v => v.Trim()).ToArray();
var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); var argumentBinder = ModelBindingTestHelper.GetArgumentBinder();
var parameter = new ParameterDescriptor() var parameter = new ParameterDescriptor
{ {
Name = "Parameter1", Name = "Parameter1",
BindingInfo = new BindingInfo() BindingInfo = new BindingInfo
{ {
BinderModelName = "CustomParameter", BinderModelName = "CustomParameter",
BindingSource = BindingSource.Header BindingSource = BindingSource.Header
@ -180,7 +177,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
ParameterType = modelType ParameterType = modelType
}; };
Action<HttpRequest> action = (r) => r.Headers.Add("CustomParameter", new[] { value }); Action<HttpRequest> action = r => r.Headers.Add("CustomParameter", new[] { value });
var operationContext = ModelBindingTestHelper.GetOperationBindingContext(action); var operationContext = ModelBindingTestHelper.GetOperationBindingContext(action);
// Do not add any headers. // Do not add any headers.
@ -202,12 +199,12 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
// ModelState // ModelState
Assert.True(modelState.IsValid); Assert.True(modelState.IsValid);
var key = Assert.Single(modelState.Keys); var entry = Assert.Single(modelState);
Assert.Equal("CustomParameter", key); Assert.Equal("CustomParameter", entry.Key);
Assert.Empty(entry.Value.Errors);
Assert.NotNull(modelState[key].Value); Assert.Equal(ModelValidationState.Valid, entry.Value.ValidationState);
Assert.Equal(expectedValue, modelState[key].Value.RawValue); Assert.Equal(value, entry.Value.Value.AttemptedValue);
Assert.Equal(value, modelState[key].Value.AttemptedValue); Assert.Equal(expectedValue, entry.Value.Value.RawValue);
} }
} }
} }

View File

@ -44,7 +44,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
public string Street { get; set; } public string Street { get; set; }
} }
[Fact(Skip = "ModelState.Value not set due to #2445.")] [Fact]
public async Task MutableObjectModelBinder_BindsNestedPOCO_WithBodyModelBinder_WithPrefix_Success() public async Task MutableObjectModelBinder_BindsNestedPOCO_WithBodyModelBinder_WithPrefix_Success()
{ {
// Arrange // Arrange
@ -85,13 +85,12 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
Assert.Equal("bill", entry.Value.AttemptedValue); Assert.Equal("bill", entry.Value.AttemptedValue);
Assert.Equal("bill", entry.Value.RawValue); Assert.Equal("bill", entry.Value.RawValue);
// These fail due to #2445
entry = Assert.Single(modelState, e => e.Key == "parameter.Customer.Address").Value; entry = Assert.Single(modelState, e => e.Key == "parameter.Customer.Address").Value;
Assert.Null(entry.Value.AttemptedValue); // ModelState entries for body don't include original text. Assert.Null(entry.Value.AttemptedValue); // ModelState entries for body don't include original text.
Assert.Same(model.Customer.Address, entry.Value.RawValue); Assert.Same(model.Customer.Address, entry.Value.RawValue);
} }
[Fact(Skip = "ModelState.Value not set due to #2445.")] [Fact]
public async Task MutableObjectModelBinder_BindsNestedPOCO_WithBodyModelBinder_WithEmptyPrefix_Success() public async Task MutableObjectModelBinder_BindsNestedPOCO_WithBodyModelBinder_WithEmptyPrefix_Success()
{ {
// Arrange // Arrange
@ -132,7 +131,6 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
Assert.Equal("bill", entry.Value.AttemptedValue); Assert.Equal("bill", entry.Value.AttemptedValue);
Assert.Equal("bill", entry.Value.RawValue); Assert.Equal("bill", entry.Value.RawValue);
// These fail due to #2445
entry = Assert.Single(modelState, e => e.Key == "Customer.Address").Value; entry = Assert.Single(modelState, e => e.Key == "Customer.Address").Value;
Assert.Null(entry.Value.AttemptedValue); // ModelState entries for body don't include original text. Assert.Null(entry.Value.AttemptedValue); // ModelState entries for body don't include original text.
Assert.Same(model.Customer.Address, entry.Value.RawValue); Assert.Same(model.Customer.Address, entry.Value.RawValue);
@ -170,11 +168,14 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
Assert.Equal("bill", model.Customer.Name); Assert.Equal("bill", model.Customer.Name);
Assert.Null(model.Customer.Address); Assert.Null(model.Customer.Address);
Assert.Equal(1, modelState.Count); Assert.Equal(2, modelState.Count);
Assert.Equal(0, modelState.ErrorCount); Assert.Equal(0, modelState.ErrorCount);
Assert.True(modelState.IsValid); Assert.True(modelState.IsValid);
var entry = Assert.Single(modelState, e => e.Key == "parameter.Customer.Name").Value; var entry = Assert.Single(modelState, e => e.Key == "parameter.Customer.Address").Value;
Assert.Null(entry.Value.AttemptedValue);
Assert.Null(entry.Value.RawValue);
entry = Assert.Single(modelState, e => e.Key == "parameter.Customer.Name").Value;
Assert.Equal("bill", entry.Value.AttemptedValue); Assert.Equal("bill", entry.Value.AttemptedValue);
Assert.Equal("bill", entry.Value.RawValue); Assert.Equal("bill", entry.Value.RawValue);
} }
@ -446,7 +447,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
public byte[] Token { get; set; } public byte[] Token { get; set; }
} }
[Fact(Skip = "Greedy model binders should set value. #2445")] [Fact]
public async Task MutableObjectModelBinder_BindsNestedPOCO_WithByteArrayModelBinder_WithPrefix_Success() public async Task MutableObjectModelBinder_BindsNestedPOCO_WithByteArrayModelBinder_WithPrefix_Success()
{ {
// Arrange // Arrange
@ -478,7 +479,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
Assert.Equal("bill", model.Customer.Name); Assert.Equal("bill", model.Customer.Name);
Assert.Equal(ByteArrayContent, model.Customer.Token); Assert.Equal(ByteArrayContent, model.Customer.Token);
Assert.Equal(2, modelState.Count); // This fails due to #2445 Assert.Equal(2, modelState.Count);
Assert.Equal(0, modelState.ErrorCount); Assert.Equal(0, modelState.ErrorCount);
Assert.True(modelState.IsValid); Assert.True(modelState.IsValid);
@ -486,13 +487,12 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
Assert.Equal("bill", entry.Value.AttemptedValue); Assert.Equal("bill", entry.Value.AttemptedValue);
Assert.Equal("bill", entry.Value.RawValue); Assert.Equal("bill", entry.Value.RawValue);
// These fail due to #2445
entry = Assert.Single(modelState, e => e.Key == "parameter.Customer.Token").Value; entry = Assert.Single(modelState, e => e.Key == "parameter.Customer.Token").Value;
Assert.Equal(ByteArrayEncoded, entry.Value.AttemptedValue); Assert.Equal(ByteArrayEncoded, entry.Value.AttemptedValue);
Assert.Equal(ByteArrayEncoded, entry.Value.RawValue); Assert.Equal(ByteArrayEncoded, entry.Value.RawValue);
} }
[Fact(Skip = "Greedy model binders should set value. #2445")] [Fact]
public async Task MutableObjectModelBinder_BindsNestedPOCO_WithByteArrayModelBinder_WithEmptyPrefix_Success() public async Task MutableObjectModelBinder_BindsNestedPOCO_WithByteArrayModelBinder_WithEmptyPrefix_Success()
{ {
// Arrange // Arrange
@ -544,17 +544,13 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
var parameter = new ParameterDescriptor() var parameter = new ParameterDescriptor()
{ {
Name = "parameter", Name = "parameter",
ParameterType = typeof(Order1) ParameterType = typeof(Order3)
}; };
// Need to have a key here so that the MutableObjectModelBinder will recurse to bind elements. // Need to have a key here so that the MutableObjectModelBinder will recurse to bind elements.
var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request => var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request =>
{ {
request.QueryString = new QueryString("?parameter.Customer.Name=bill"); request.QueryString = new QueryString("?parameter.Customer.Name=bill");
// This is set so that the input formatter does not add an error to model state.
// Thus this prevents addition of an extra error unrelated to the test scenario.
request.ContentType = "application/json";
}); });
var modelState = new ModelStateDictionary(); var modelState = new ModelStateDictionary();
@ -566,10 +562,10 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
Assert.NotNull(modelBindingResult); Assert.NotNull(modelBindingResult);
Assert.True(modelBindingResult.IsModelSet); Assert.True(modelBindingResult.IsModelSet);
var model = Assert.IsType<Order1>(modelBindingResult.Model); var model = Assert.IsType<Order3>(modelBindingResult.Model);
Assert.NotNull(model.Customer); Assert.NotNull(model.Customer);
Assert.Equal("bill", model.Customer.Name); Assert.Equal("bill", model.Customer.Name);
Assert.Null(model.Customer.Address); Assert.Null(model.Customer.Token);
Assert.Equal(1, modelState.Count); Assert.Equal(1, modelState.Count);
Assert.Equal(0, modelState.ErrorCount); Assert.Equal(0, modelState.ErrorCount);
@ -594,7 +590,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
public IEnumerable<IFormFile> Documents { get; set; } public IEnumerable<IFormFile> Documents { get; set; }
} }
[Fact(Skip = "Greedy model binders should set value. #2445")] [Fact]
public async Task MutableObjectModelBinder_BindsNestedPOCO_WithFormFileModelBinder_WithPrefix_Success() public async Task MutableObjectModelBinder_BindsNestedPOCO_WithFormFileModelBinder_WithPrefix_Success()
{ {
// Arrange // Arrange
@ -626,7 +622,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
Assert.Equal("bill", model.Customer.Name); Assert.Equal("bill", model.Customer.Name);
Assert.Single(model.Customer.Documents); Assert.Single(model.Customer.Documents);
Assert.Equal(2, modelState.Count); // This fails due to #2445 Assert.Equal(2, modelState.Count);
Assert.Equal(0, modelState.ErrorCount); Assert.Equal(0, modelState.ErrorCount);
Assert.True(modelState.IsValid); Assert.True(modelState.IsValid);
@ -639,7 +635,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
Assert.Same(model.Customer.Documents, entry.Value.RawValue); Assert.Same(model.Customer.Documents, entry.Value.RawValue);
} }
[Fact(Skip = "Greedy model binders should set value. #2445")] [Fact]
public async Task MutableObjectModelBinder_BindsNestedPOCO_WithFormFileModelBinder_WithEmptyPrefix_Success() public async Task MutableObjectModelBinder_BindsNestedPOCO_WithFormFileModelBinder_WithEmptyPrefix_Success()
{ {
// Arrange // Arrange
@ -717,11 +713,16 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
Assert.Equal("bill", model.Customer.Name); Assert.Equal("bill", model.Customer.Name);
Assert.Empty(model.Customer.Documents); Assert.Empty(model.Customer.Documents);
Assert.Equal(1, modelState.Count); Assert.Equal(2, modelState.Count);
Assert.Equal(0, modelState.ErrorCount); Assert.Equal(0, modelState.ErrorCount);
Assert.True(modelState.IsValid); Assert.True(modelState.IsValid);
var entry = Assert.Single(modelState, e => e.Key == "parameter.Customer.Name").Value; var entry = Assert.Single(modelState, e => e.Key == "parameter.Customer.Documents").Value;
Assert.Null(entry.Value.AttemptedValue);
var documents = Assert.IsAssignableFrom<IEnumerable<IFormFile>>(entry.Value.RawValue);
Assert.Empty(documents);
entry = Assert.Single(modelState, e => e.Key == "parameter.Customer.Name").Value;
Assert.Equal("bill", entry.Value.AttemptedValue); Assert.Equal("bill", entry.Value.AttemptedValue);
Assert.Equal("bill", entry.Value.RawValue); Assert.Equal("bill", entry.Value.RawValue);
} }
@ -1529,7 +1530,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
// If a nested POCO object has all properties bound from a greedy source, then it should be populated // If a nested POCO object has all properties bound from a greedy source, then it should be populated
// if the top-level object is created. // if the top-level object is created.
[Fact(Skip = "ModelState.Value not set due to #2445.")] [Fact]
public async Task MutableObjectModelBinder_BindsNestedPOCO_WithAllGreedyBoundProperties() public async Task MutableObjectModelBinder_BindsNestedPOCO_WithAllGreedyBoundProperties()
{ {
// Arrange // Arrange
@ -1567,7 +1568,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
Assert.True(modelState.IsValid); Assert.True(modelState.IsValid);
var entry = Assert.Single(modelState, e => e.Key == "Customer.Address").Value; var entry = Assert.Single(modelState, e => e.Key == "Customer.Address").Value;
Assert.Null(entry.Value.AttemptedValue); // This fails due to #2445 Assert.Null(entry.Value.AttemptedValue);
Assert.Same(model.Customer.Address, entry.Value.RawValue); Assert.Same(model.Customer.Address, entry.Value.RawValue);
} }

View File

@ -31,7 +31,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
Name = "parameter", Name = "parameter",
ParameterType = typeof(Order1) ParameterType = typeof(Order1)
}; };
var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request => var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request =>
{ {
request.QueryString = new QueryString("?parameter.CustomerName=bill"); request.QueryString = new QueryString("?parameter.CustomerName=bill");
@ -1020,6 +1020,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
private class Address private class Address
{ {
public int Street { get; set; } public int Street { get; set; }
public string State { get; set; } public string State { get; set; }
[Range(10000, 99999)] [Range(10000, 99999)]
@ -1032,30 +1033,32 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
{ {
public string Name { get; set; } public string Name { get; set; }
} }
[Fact] [Fact]
public async Task TypeBasedExclusion_ForBodyAndNonBodyBoundModels() public async Task TypeBasedExclusion_ForBodyAndNonBodyBoundModels()
{ {
// Arrange // Arrange
var parameter = new ParameterDescriptor() var parameter = new ParameterDescriptor
{ {
Name = "parameter", Name = "parameter",
ParameterType = typeof(Order11) ParameterType = typeof(Order11)
}; };
MvcOptions testOptions = null; MvcOptions testOptions = null;
var input = "{\"OfficeAddress.Zip\":\"45\"}"; var input = "{\"Zip\":\"47\"}";
var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request => var operationContext = ModelBindingTestHelper.GetOperationBindingContext(
{ request =>
request.QueryString = {
new QueryString("?HomeAddress.Country.Name=US&ShippingAddresses[0].Zip=45&HomeAddress.Zip=46"); request.QueryString =
request.Body = new MemoryStream(Encoding.UTF8.GetBytes(input)); new QueryString("?HomeAddress.Country.Name=US&ShippingAddresses[0].Zip=45&HomeAddress.Zip=46");
request.ContentType = "application/json"; request.Body = new MemoryStream(Encoding.UTF8.GetBytes(input));
}, request.ContentType = "application/json";
options => { },
options =>
options.ValidationExcludeFilters.Add(typeof(Address)); {
testOptions = options; options.ValidationExcludeFilters.Add(typeof(Address));
}); testOptions = options;
});
var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(testOptions); var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(testOptions);
var modelState = new ModelStateDictionary(); var modelState = new ModelStateDictionary();
@ -1063,7 +1066,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
// Act // Act
var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext); var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext);
Assert.Equal(3, modelState.Count); Assert.Equal(4, modelState.Count);
Assert.Equal(0, modelState.ErrorCount); Assert.Equal(0, modelState.ErrorCount);
Assert.True(modelState.IsValid); Assert.True(modelState.IsValid);
@ -1081,6 +1084,14 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
Assert.Equal("46", entry.Value.AttemptedValue); Assert.Equal("46", entry.Value.AttemptedValue);
Assert.Equal("46", entry.Value.RawValue); Assert.Equal("46", entry.Value.RawValue);
Assert.Equal(ModelValidationState.Skipped, entry.ValidationState); Assert.Equal(ModelValidationState.Skipped, entry.ValidationState);
entry = Assert.Single(modelState, e => e.Key == "OfficeAddress").Value;
Assert.Null(entry.Value.AttemptedValue);
var address = Assert.IsType<Address>(entry.Value.RawValue);
Assert.Equal(47, address.Zip);
// Address itself is not excluded from validation.
Assert.Equal(ModelValidationState.Valid, entry.ValidationState);
} }
private static void AssertRequiredError(string key, ModelError error) private static void AssertRequiredError(string key, ModelError error)