Validations with BindRequired on properties.

This commit is contained in:
Harsh Gupta 2015-05-01 14:30:28 -07:00
parent 502b832d17
commit f37f2ae352
2 changed files with 658 additions and 1 deletions

View File

@ -2,7 +2,6 @@
// 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.ObjectModel;
using System.Threading.Tasks;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Mvc.ModelBinding;
@ -338,5 +337,204 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
Assert.Equal(0, modelState.ErrorCount);
Assert.True(modelState.IsValid);
}
private class Person2
{
public int Id { get; set; }
[BindRequired]
public string Name { get; set; }
}
[Fact]
public async Task CollectionModelBinder_BindsListOfComplexType_WithRequiredProperty_WithPrefix_PartialData()
{
// Arrange
var argumentBinder = ModelBindingTestHelper.GetArgumentBinder();
var parameter = new ParameterDescriptor()
{
Name = "parameter",
ParameterType = typeof(List<Person2>)
};
var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request =>
{
request.QueryString = new QueryString("?parameter[0].Id=10&parameter[1].Id=11");
});
var modelState = new ModelStateDictionary();
// Act
var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext);
// Assert
Assert.NotNull(modelBindingResult);
Assert.True(modelBindingResult.IsModelSet);
var model = Assert.IsType<List<Person2>>(modelBindingResult.Model);
Assert.Equal(10, model[0].Id);
Assert.Equal(11, model[1].Id);
Assert.Null(model[0].Name);
Assert.Null(model[1].Name);
Assert.Equal(4, modelState.Count);
Assert.Equal(2, modelState.ErrorCount);
Assert.False(modelState.IsValid);
var entry = Assert.Single(modelState, kvp => kvp.Key == "parameter[0].Id").Value;
Assert.Equal("10", entry.Value.AttemptedValue);
Assert.Equal("10", entry.Value.RawValue);
entry = Assert.Single(modelState, kvp => kvp.Key == "parameter[1].Id").Value;
Assert.Equal("11", entry.Value.AttemptedValue);
Assert.Equal("11", entry.Value.RawValue);
entry = Assert.Single(modelState, kvp => kvp.Key == "parameter[0].Name").Value;
Assert.Null(entry.Value);
Assert.Equal(ModelValidationState.Invalid, entry.ValidationState);
entry = Assert.Single(modelState, kvp => kvp.Key == "parameter[1].Name").Value;
Assert.Null(entry.Value);
Assert.Equal(ModelValidationState.Invalid, entry.ValidationState);
}
[Fact]
public async Task CollectionModelBinder_BindsListOfComplexType_WithRequiredProperty_WithExplicitPrefix_PartialData()
{
// Arrange
var argumentBinder = ModelBindingTestHelper.GetArgumentBinder();
var parameter = new ParameterDescriptor()
{
Name = "parameter",
BindingInfo = new BindingInfo()
{
BinderModelName = "prefix",
},
ParameterType = typeof(List<Person2>)
};
var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request =>
{
request.QueryString = new QueryString("?prefix[0].Id=10&prefix[1].Id=11");
});
var modelState = new ModelStateDictionary();
// Act
var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext);
// Assert
Assert.NotNull(modelBindingResult);
Assert.True(modelBindingResult.IsModelSet);
var model = Assert.IsType<List<Person2>>(modelBindingResult.Model);
Assert.Equal(10, model[0].Id);
Assert.Null(model[0].Name);
Assert.Equal(11, model[1].Id);
Assert.Null(model[1].Name);
Assert.Equal(4, modelState.Count);
Assert.Equal(2, modelState.ErrorCount);
Assert.False(modelState.IsValid);
var entry = Assert.Single(modelState, kvp => kvp.Key == "prefix[0].Id").Value;
Assert.Equal("10", entry.Value.AttemptedValue);
Assert.Equal("10", entry.Value.RawValue);
entry = Assert.Single(modelState, kvp => kvp.Key == "prefix[1].Id").Value;
Assert.Equal("11", entry.Value.AttemptedValue);
Assert.Equal("11", entry.Value.RawValue);
entry = Assert.Single(modelState, kvp => kvp.Key == "prefix[0].Name").Value;
Assert.Null(entry.Value);
Assert.Equal(ModelValidationState.Invalid, entry.ValidationState);
entry = Assert.Single(modelState, kvp => kvp.Key == "prefix[1].Name").Value;
Assert.Null(entry.Value);
Assert.Equal(ModelValidationState.Invalid, entry.ValidationState);
}
[Fact]
public async Task CollectionModelBinder_BindsCollectionOfComplexType_WithRequiredProperty_EmptyPrefix_PartialData()
{
// Arrange
var argumentBinder = ModelBindingTestHelper.GetArgumentBinder();
var parameter = new ParameterDescriptor()
{
Name = "parameter",
ParameterType = typeof(ICollection<Person2>)
};
var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request =>
{
request.QueryString = new QueryString("?[0].Id=10&[1].Id=11");
});
var modelState = new ModelStateDictionary();
// Act
var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext);
// Assert
Assert.NotNull(modelBindingResult);
Assert.True(modelBindingResult.IsModelSet);
var model = Assert.IsType<List<Person2>>(modelBindingResult.Model);
Assert.Equal(10, model[0].Id);
Assert.Null(model[0].Name);
Assert.Equal(11, model[1].Id);
Assert.Null(model[1].Name);
Assert.Equal(4, modelState.Count);
Assert.Equal(2, modelState.ErrorCount);
Assert.False(modelState.IsValid);
var entry = Assert.Single(modelState, kvp => kvp.Key == "[0].Id").Value;
Assert.Equal("10", entry.Value.AttemptedValue);
Assert.Equal("10", entry.Value.RawValue);
entry = Assert.Single(modelState, kvp => kvp.Key == "[1].Id").Value;
Assert.Equal("11", entry.Value.AttemptedValue);
Assert.Equal("11", entry.Value.RawValue);
entry = Assert.Single(modelState, kvp => kvp.Key == "[0].Name").Value;
Assert.Null(entry.Value);
Assert.Equal(ModelValidationState.Invalid, entry.ValidationState);
entry = Assert.Single(modelState, kvp => kvp.Key == "[1].Name").Value;
Assert.Null(entry.Value);
Assert.Equal(ModelValidationState.Invalid, entry.ValidationState);
}
[Fact(Skip = "Empty collection should be created by the collection model binder #1579")]
public async Task CollectionModelBinder_BindsListOfComplexType_WithRequiredProperty_NoData()
{
// Arrange
var argumentBinder = ModelBindingTestHelper.GetArgumentBinder();
var parameter = new ParameterDescriptor()
{
Name = "parameter",
ParameterType = typeof(List<Person2>)
};
var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request =>
{
request.QueryString = new QueryString("?");
});
var modelState = new ModelStateDictionary();
// Act
var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext);
// Assert
Assert.NotNull(modelBindingResult); // This fails due to #1579
Assert.False(modelBindingResult.IsModelSet);
Assert.Empty(Assert.IsType<List<Person2>>(modelBindingResult.Model));
Assert.Equal(0, modelState.Count);
Assert.Equal(0, modelState.ErrorCount);
Assert.True(modelState.IsValid);
}
}
}

View File

@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.IO;
using System.Text;
using System.Threading.Tasks;
@ -1565,6 +1566,464 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
Assert.Same(model.Customer.Address, entry.Value.RawValue);
}
private class Order10
{
[BindRequired]
public Person10 Customer { get; set; }
}
private class Person10
{
public string Name { get; set; }
}
[Fact(Skip = "Error message is incorrect #2493.")]
public async Task MutableObjectModelBinder_WithRequiredComplexProperty_NoData_GetsErrors()
{
// Arrange
var argumentBinder = ModelBindingTestHelper.GetArgumentBinder();
var parameter = new ParameterDescriptor()
{
Name = "parameter",
ParameterType = typeof(Order10)
};
// No Data
var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request =>
{
});
var modelState = new ModelStateDictionary();
// Act
var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext);
// Assert
Assert.NotNull(modelBindingResult);
Assert.True(modelBindingResult.IsModelSet);
var model = Assert.IsType<Order10>(modelBindingResult.Model);
Assert.Null(model.Customer);
Assert.Equal(1, modelState.Count); // This fails due to #2446
Assert.Equal(1, modelState.ErrorCount);
Assert.False(modelState.IsValid);
var entry = Assert.Single(modelState, e => e.Key == "Customer").Value;
Assert.Null(entry.Value);
var error = Assert.Single(modelState["Customer"].Errors);
Assert.Equal("The Customer field is required.", error.ErrorMessage);
}
private class Order11
{
public Person11 Customer { get; set; }
}
private class Person11
{
public int Id { get; set; }
[BindRequired]
public string Name { get; set; }
}
[Fact(Skip = "Error message is incorrect #2493.")]
public async Task MutableObjectModelBinder_WithNestedRequiredProperty_WithPartialData_GetsErrors()
{
// Arrange
var argumentBinder = ModelBindingTestHelper.GetArgumentBinder();
var parameter = new ParameterDescriptor()
{
Name = "parameter",
ParameterType = typeof(Order11)
};
// No Data
var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request =>
{
request.QueryString = new QueryString("?parameter.Customer.Id=123");
});
var modelState = new ModelStateDictionary();
// Act
var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext);
// Assert
Assert.NotNull(modelBindingResult);
Assert.True(modelBindingResult.IsModelSet);
var model = Assert.IsType<Order11>(modelBindingResult.Model);
Assert.NotNull(model.Customer);
Assert.Equal(123, model.Customer.Id);
Assert.Null(model.Customer.Name);
Assert.Equal(2, modelState.Count); // This fails due to #2446
Assert.Equal(1, modelState.ErrorCount);
Assert.False(modelState.IsValid);
var entry = Assert.Single(modelState, e => e.Key == "parameter.Customer.Id").Value;
Assert.NotNull(entry.Value);
Assert.Equal("123", entry.Value.RawValue);
Assert.Equal("123", entry.Value.AttemptedValue);
entry = Assert.Single(modelState, e => e.Key == "parameter.Customer.Name").Value;
Assert.Null(entry.Value);
var error = Assert.Single(modelState["parameter.Customer.Name"].Errors);
Assert.Equal("The Name field is required.", error.ErrorMessage);
}
[Fact(Skip = "Error message is incorrect #2493.")]
public async Task MutableObjectModelBinder_WithNestedRequiredProperty_WithData_EmptyPrefix_GetsErrors()
{
// Arrange
var argumentBinder = ModelBindingTestHelper.GetArgumentBinder();
var parameter = new ParameterDescriptor()
{
Name = "parameter",
ParameterType = typeof(Order11)
};
// No Data
var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request =>
{
request.QueryString = new QueryString("?Customer.Id=123");
});
var modelState = new ModelStateDictionary();
// Act
var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext);
// Assert
Assert.NotNull(modelBindingResult);
Assert.True(modelBindingResult.IsModelSet);
var model = Assert.IsType<Order11>(modelBindingResult.Model);
Assert.NotNull(model.Customer);
Assert.Equal(123, model.Customer.Id);
Assert.Null(model.Customer.Name);
Assert.Equal(2, modelState.Count); // This fails due to #2446
Assert.Equal(1, modelState.ErrorCount);
Assert.False(modelState.IsValid);
var entry = Assert.Single(modelState, e => e.Key == "Customer.Id").Value;
Assert.NotNull(entry.Value);
Assert.Equal("123", entry.Value.RawValue);
Assert.Equal("123", entry.Value.AttemptedValue);
entry = Assert.Single(modelState, e => e.Key == "Customer.Name").Value;
Assert.Null(entry.Value);
var error = Assert.Single(modelState["Customer.Name"].Errors);
Assert.Equal("The Name field is required.", error.ErrorMessage);
}
[Fact(Skip = "Error message is incorrect #2493.")]
public async Task MutableObjectModelBinder_WithNestedRequiredProperty_WithData_CustomPrefix_GetsErrors()
{
// Arrange
var argumentBinder = ModelBindingTestHelper.GetArgumentBinder();
var parameter = new ParameterDescriptor()
{
Name = "parameter",
ParameterType = typeof(Order11),
BindingInfo = new BindingInfo()
{
BinderModelName = "customParameter"
}
};
// No Data
var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request =>
{
request.QueryString = new QueryString("?customParameter.Customer.Id=123");
});
var modelState = new ModelStateDictionary();
// Act
var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext);
// Assert
Assert.NotNull(modelBindingResult);
Assert.True(modelBindingResult.IsModelSet);
var model = Assert.IsType<Order11>(modelBindingResult.Model);
Assert.NotNull(model.Customer);
Assert.Equal(123, model.Customer.Id);
Assert.Null(model.Customer.Name);
Assert.Equal(2, modelState.Count); // This fails due to #2446
Assert.Equal(1, modelState.ErrorCount);
Assert.False(modelState.IsValid);
var entry = Assert.Single(modelState, e => e.Key == "customParameter.Customer.Id").Value;
Assert.NotNull(entry.Value);
Assert.Equal("123", entry.Value.RawValue);
Assert.Equal("123", entry.Value.AttemptedValue);
entry = Assert.Single(modelState, e => e.Key == "customParameter.Customer.Name").Value;
Assert.Null(entry.Value);
var error = Assert.Single(modelState["customParameter.Customer.Name"].Errors);
Assert.Equal("The Name field is required.", error.ErrorMessage);
}
private class Order12
{
[BindRequired]
public string ProductName { get; set; }
}
[Fact(Skip = "Error message is incorrect #2493.")]
public async Task MutableObjectModelBinder_WithRequiredProperty_NoData_GetsErrors()
{
// Arrange
var argumentBinder = ModelBindingTestHelper.GetArgumentBinder();
var parameter = new ParameterDescriptor()
{
Name = "parameter",
ParameterType = typeof(Order12)
};
// No Data
var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request =>
{
request.QueryString = new QueryString("?");
});
var modelState = new ModelStateDictionary();
// Act
var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext);
// Assert
Assert.NotNull(modelBindingResult);
Assert.True(modelBindingResult.IsModelSet);
var model = Assert.IsType<Order12>(modelBindingResult.Model);
Assert.Null(model.ProductName);
Assert.Equal(1, modelState.Count); // This fails due to #2446
Assert.Equal(1, modelState.ErrorCount);
Assert.False(modelState.IsValid);
var entry = Assert.Single(modelState, e => e.Key == "ProductName").Value;
Assert.Null(entry.Value);
var error = Assert.Single(modelState["ProductName"].Errors);
Assert.Equal("The ProductName field is required.", error.ErrorMessage);
}
[Fact(Skip = "Error message is incorrect #2493.")]
public async Task MutableObjectModelBinder_WithRequiredProperty_NoData_CustomPrefix_GetsErros()
{
// Arrange
var argumentBinder = ModelBindingTestHelper.GetArgumentBinder();
var parameter = new ParameterDescriptor()
{
Name = "parameter",
ParameterType = typeof(Order12),
BindingInfo = new BindingInfo()
{
BinderModelName = "customParameter"
}
};
// No Data
var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request =>
{
request.QueryString = new QueryString("?");
});
var modelState = new ModelStateDictionary();
// Act
var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext);
// Assert
Assert.NotNull(modelBindingResult);
Assert.True(modelBindingResult.IsModelSet);
var model = Assert.IsType<Order12>(modelBindingResult.Model);
Assert.Null(model.ProductName);
Assert.Equal(1, modelState.Count);
Assert.Equal(1, modelState.ErrorCount);
Assert.False(modelState.IsValid);
var entry = Assert.Single(modelState, e => e.Key == "customParameter.ProductName").Value;
Assert.Null(entry.Value);
var error = Assert.Single(modelState["customParameter.ProductName"].Errors);
Assert.Equal("The ProductName field is required.", error.ErrorMessage);
}
[Fact(Skip = "Extra model state entry due to #2446")]
public async Task MutableObjectModelBinder_WithRequiredProperty_WithData_EmptyPrefix_GetsBound()
{
// Arrange
var argumentBinder = ModelBindingTestHelper.GetArgumentBinder();
var parameter = new ParameterDescriptor()
{
Name = "parameter",
ParameterType = typeof(Order12),
};
// No Data
var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request =>
{
request.QueryString = new QueryString("?ProductName=abc");
});
var modelState = new ModelStateDictionary();
// Act
var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext);
// Assert
Assert.NotNull(modelBindingResult);
Assert.True(modelBindingResult.IsModelSet);
var model = Assert.IsType<Order12>(modelBindingResult.Model);
Assert.Equal("abc", model.ProductName);
Assert.Equal(1, modelState.Count); // This fails due to #2446
Assert.Equal(0, modelState.ErrorCount);
Assert.True(modelState.IsValid);
var entry = Assert.Single(modelState, e => e.Key == "ProductName").Value;
Assert.NotNull(entry.Value);
Assert.Equal("abc", entry.Value.RawValue);
Assert.Equal("abc", entry.Value.AttemptedValue);
}
private class Order13
{
[BindRequired]
public List<int> OrderIds { get; set; }
}
[Fact(Skip = "Error message is incorrect #2493.")]
public async Task MutableObjectModelBinder_WithRequiredCollectionProperty_NoData_GetsErros()
{
// Arrange
var argumentBinder = ModelBindingTestHelper.GetArgumentBinder();
var parameter = new ParameterDescriptor()
{
Name = "parameter",
ParameterType = typeof(Order13)
};
// No Data
var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request =>
{
request.QueryString = new QueryString("?");
});
var modelState = new ModelStateDictionary();
// Act
var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext);
// Assert
Assert.NotNull(modelBindingResult);
Assert.True(modelBindingResult.IsModelSet);
var model = Assert.IsType<Order13>(modelBindingResult.Model);
Assert.Null(model.OrderIds);
Assert.Equal(1, modelState.Count);
Assert.Equal(1, modelState.ErrorCount);
Assert.False(modelState.IsValid);
var entry = Assert.Single(modelState, e => e.Key == "OrderIds").Value;
Assert.Null(entry.Value);
var error = Assert.Single(modelState["OrderIds"].Errors);
Assert.Equal("The OrderIds field is required.", error.ErrorMessage);
}
[Fact(Skip = "Error message is incorrect #2493.")]
public async Task MutableObjectModelBinder_WithRequiredCollectionProperty_NoData_CustomPrefix_GetsErros()
{
// Arrange
var argumentBinder = ModelBindingTestHelper.GetArgumentBinder();
var parameter = new ParameterDescriptor()
{
Name = "parameter",
ParameterType = typeof(Order13),
BindingInfo = new BindingInfo()
{
BinderModelName = "customParameter"
}
};
// No Data
var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request =>
{
request.QueryString = new QueryString("?");
});
var modelState = new ModelStateDictionary();
// Act
var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext);
// Assert
Assert.NotNull(modelBindingResult);
Assert.True(modelBindingResult.IsModelSet);
var model = Assert.IsType<Order13>(modelBindingResult.Model);
Assert.Null(model.OrderIds);
Assert.Equal(1, modelState.Count);
Assert.Equal(1, modelState.ErrorCount);
Assert.False(modelState.IsValid);
var entry = Assert.Single(modelState, e => e.Key == "customParameter.OrderIds").Value;
Assert.Null(entry.Value);
var error = Assert.Single(modelState["customParameter.OrderIds"].Errors);
Assert.Equal("The OrderIds field is required.", error.ErrorMessage);
}
[Fact(Skip = "Extra model state entry due to #2446")]
public async Task MutableObjectModelBinder_WithRequiredCollectionProperty_WithData_EmptyPrefix_GetsBound()
{
// Arrange
var argumentBinder = ModelBindingTestHelper.GetArgumentBinder();
var parameter = new ParameterDescriptor()
{
Name = "parameter",
ParameterType = typeof(Order13),
};
// No Data
var operationContext = ModelBindingTestHelper.GetOperationBindingContext(request =>
{
request.QueryString = new QueryString("?OrderIds[0]=123");
});
var modelState = new ModelStateDictionary();
// Act
var modelBindingResult = await argumentBinder.BindModelAsync(parameter, modelState, operationContext);
// Assert
Assert.NotNull(modelBindingResult);
Assert.True(modelBindingResult.IsModelSet);
var model = Assert.IsType<Order13>(modelBindingResult.Model);
Assert.Equal(new[] { 123 }, model.OrderIds.ToArray());
Assert.Equal(1, modelState.Count); // This fails due to #2446
Assert.Equal(0, modelState.ErrorCount);
Assert.True(modelState.IsValid);
var entry = Assert.Single(modelState, e => e.Key == "OrderIds[0]").Value;
Assert.NotNull(entry.Value);
Assert.Equal("123", entry.Value.RawValue);
Assert.Equal("123", entry.Value.AttemptedValue);
}
private static void SetJsonBodyContent(HttpRequest request, string content)
{
var stream = new MemoryStream(new UTF8Encoding(encoderShouldEmitUTF8Identifier: false).GetBytes(content));