diff --git a/src/Microsoft.AspNet.Mvc.Xml/RequiredValidationHelper.cs b/src/Microsoft.AspNet.Mvc.Xml/RequiredValidationHelper.cs
deleted file mode 100644
index 6d1ce483a5..0000000000
--- a/src/Microsoft.AspNet.Mvc.Xml/RequiredValidationHelper.cs
+++ /dev/null
@@ -1,212 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using System;
-using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.ComponentModel.DataAnnotations;
-using System.Reflection;
-using System.Runtime.Serialization;
-using Microsoft.AspNet.Mvc.ModelBinding;
-using Microsoft.Framework.Internal;
-
-namespace Microsoft.AspNet.Mvc.Xml
-{
- ///
- /// Validates types having value type properties decorated with
- /// but no .
- ///
- ///
- /// supports where as the xml formatters
- /// do not. Since a user's aplication can have both Json and Xml formatters, a request could be validated
- /// when posted as Json but not Xml. So to prevent end users from having a false sense of security when posting
- /// as Xml, we add errors to model-state to at least let the users know that there is a problem with their models.
- ///
- public class DataAnnotationRequiredAttributeValidation
- {
- // Since formatters are 'typically' registered as single instance, concurrent dictionary is used
- // here to avoid duplicate errors being added for a type.
- private ConcurrentDictionary>> _cachedValidationErrors
- = new ConcurrentDictionary>>();
-
- public void Validate([NotNull] Type modelType, [NotNull] ModelStateDictionary modelStateDictionary)
- {
- var visitedTypes = new HashSet();
-
- // Every node maintains a dictionary of Type => Errors.
- // It's a dictionary as we want to avoid adding duplicate error messages.
- // Example:
- // In the following case, from the perspective of type 'Store', we should not see duplicate
- // errors related to type 'Address'
- // public class Store
- // {
- // [Required]
- // public int Id { get; set; }
- // public Address Address { get; set; }
- // }
- // public class Employee
- // {
- // [Required]
- // public int Id { get; set; }
- // public Address Address { get; set; }
- // }
- // public class Address
- // {
- // [Required]
- // public string Line1 { get; set; }
- // [Required]
- // public int Zipcode { get; set; }
- // [Required]
- // public string State { get; set; }
- // }
- var rootNodeValidationErrors = new Dictionary>();
-
- Validate(modelType, visitedTypes, rootNodeValidationErrors);
-
- foreach (var validationError in rootNodeValidationErrors)
- {
- foreach (var validationErrorMessage in validationError.Value)
- {
- // Add error message to model state as exception to avoid
- // disclosing details to end user as SerializableError sanitizes the
- // model state errors having exceptions with a generic message when sending
- // it to the client.
- modelStateDictionary.TryAddModelError(
- validationError.Key.FullName,
- new InvalidOperationException(validationErrorMessage));
- }
- }
- }
-
- private void Validate(
- Type modelType,
- HashSet visitedTypes,
- Dictionary> errors)
- {
- // We don't need to code special handling for KeyValuePair (for example, when the model type
- // is Dictionary<,> which implements IEnumerable>) as the model
- // type here would be KeyValuePair where Key and Value are public properties
- // which would also be probed for Required attribute validation.
- if (modelType.GetTypeInfo().IsGenericType)
- {
- var enumerableOfT = ClosedGenericMatcher.ExtractGenericInterface(modelType, typeof(IEnumerable<>));
- if (enumerableOfT != null)
- {
- modelType = enumerableOfT.GenericTypeArguments[0];
- }
- }
-
- if (ExcludeTypeFromValidation(modelType))
- {
- return;
- }
-
- // Avoid infinite loop in case of self-referencing properties
- if (!visitedTypes.Add(modelType))
- {
- return;
- }
-
- Dictionary> cachedErrors;
- if (_cachedValidationErrors.TryGetValue(modelType, out cachedErrors))
- {
- foreach (var validationError in cachedErrors)
- {
- errors.Add(validationError.Key, validationError.Value);
- }
-
- return;
- }
-
- foreach (var propertyHelper in PropertyHelper.GetProperties(modelType))
- {
- var propertyInfo = propertyHelper.Property;
- var propertyType = propertyInfo.PropertyType;
-
- // Since DefaultObjectValidator can handle Required attribute validation for reference types,
- // we only consider value types here.
- if (propertyType.GetTypeInfo().IsValueType && !TypeHelper.IsNullableValueType(propertyType))
- {
- var validationError = GetValidationError(propertyInfo);
- if (validationError != null)
- {
- List errorMessages;
- if (!errors.TryGetValue(validationError.ModelType, out errorMessages))
- {
- errorMessages = new List();
- errors.Add(validationError.ModelType, errorMessages);
- }
-
- errorMessages.Add(Resources.FormatRequiredProperty_MustHaveDataMemberRequired(
- typeof(DataContractSerializer).FullName,
- typeof(RequiredAttribute).FullName,
- typeof(DataMemberAttribute).FullName,
- nameof(DataMemberAttribute.IsRequired),
- bool.TrueString,
- validationError.PropertyName,
- validationError.ModelType.FullName));
- }
-
- // if the type is not primitve, then it could be a struct in which case
- // we need to probe its properties for validation
- if (propertyType.GetTypeInfo().IsPrimitive)
- {
- continue;
- }
- }
-
- var childNodeErrors = new Dictionary>();
- Validate(propertyType, visitedTypes, childNodeErrors);
-
- // Avoid adding duplicate errors at current node.
- foreach (var modelTypeKey in childNodeErrors.Keys)
- {
- if (!errors.ContainsKey(modelTypeKey))
- {
- errors.Add(modelTypeKey, childNodeErrors[modelTypeKey]);
- }
- }
- }
-
- _cachedValidationErrors.TryAdd(modelType, errors);
-
- visitedTypes.Remove(modelType);
- }
-
- private ValidationError GetValidationError(PropertyInfo propertyInfo)
- {
- var required = propertyInfo.GetCustomAttribute(typeof(RequiredAttribute), inherit: true);
- if (required == null)
- {
- return null;
- }
-
- var dataMemberRequired = (DataMemberAttribute)propertyInfo.GetCustomAttribute(
- typeof(DataMemberAttribute),
- inherit: true);
-
- if (dataMemberRequired != null && dataMemberRequired.IsRequired)
- {
- return null;
- }
-
- return new ValidationError()
- {
- ModelType = propertyInfo.DeclaringType,
- PropertyName = propertyInfo.Name
- };
- }
-
- private bool ExcludeTypeFromValidation(Type modelType)
- {
- return TypeHelper.IsSimpleType(modelType);
- }
-
- private class ValidationError
- {
- public Type ModelType { get; set; }
-
- public string PropertyName { get; set; }
- }
- }
-}
diff --git a/src/Microsoft.AspNet.Mvc.Xml/XmlDataContractSerializerInputFormatter.cs b/src/Microsoft.AspNet.Mvc.Xml/XmlDataContractSerializerInputFormatter.cs
index 6e3aff7c51..2e23668db0 100644
--- a/src/Microsoft.AspNet.Mvc.Xml/XmlDataContractSerializerInputFormatter.cs
+++ b/src/Microsoft.AspNet.Mvc.Xml/XmlDataContractSerializerInputFormatter.cs
@@ -24,7 +24,6 @@ namespace Microsoft.AspNet.Mvc.Xml
private DataContractSerializerSettings _serializerSettings;
private ConcurrentDictionary _serializerCache = new ConcurrentDictionary();
private readonly XmlDictionaryReaderQuotas _readerQuotas = FormattingUtilities.GetDefaultXmlReaderQuotas();
- private readonly DataAnnotationRequiredAttributeValidation _dataAnnotationRequiredAttributeValidation;
///
/// Initializes a new instance of DataContractSerializerInputFormatter
@@ -41,8 +40,6 @@ namespace Microsoft.AspNet.Mvc.Xml
WrapperProviderFactories = new List();
WrapperProviderFactories.Add(new SerializableErrorWrapperProviderFactory());
-
- _dataAnnotationRequiredAttributeValidation = new DataAnnotationRequiredAttributeValidation();
}
///
@@ -103,11 +100,6 @@ namespace Microsoft.AspNet.Mvc.Xml
using (var xmlReader = CreateXmlReader(new NonDisposableStream(request.Body), effectiveEncoding))
{
var type = GetSerializableType(context.ModelType);
-
- _dataAnnotationRequiredAttributeValidation.Validate(
- type,
- context.ModelState);
-
var serializer = GetCachedSerializer(type);
var deserializedObject = serializer.ReadObject(xmlReader);
diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/XmlDataContractSerializerInputFormatterTest.cs b/test/Microsoft.AspNet.Mvc.FunctionalTests/XmlDataContractSerializerInputFormatterTest.cs
index d672dae7ed..13188033d2 100644
--- a/test/Microsoft.AspNet.Mvc.FunctionalTests/XmlDataContractSerializerInputFormatterTest.cs
+++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/XmlDataContractSerializerInputFormatterTest.cs
@@ -54,10 +54,8 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
data);
}
- // Verifies that even though all the required data is posted to an action, the model
- // state has errors related to value types's Required attribute validation.
[Fact]
- public async Task RequiredDataIsProvided_AndModelIsBound_AndHasRequiredAttributeValidationErrors()
+ public async Task RequiredDataIsProvided_AndModelIsBound_NoValidationErrors()
{
// Arrange
var server = TestHelper.CreateServer(_app, SiteName, _configureServices);
@@ -67,13 +65,6 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
"xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">WA" +
"9805210";
var content = new StringContent(input, Encoding.UTF8, "application/xml-dcs");
- var propertiesCollection = new List>();
- propertiesCollection.Add(new KeyValuePair(nameof(Store.Id), typeof(Store).FullName));
- propertiesCollection.Add(new KeyValuePair(nameof(Address.Zipcode), typeof(Address).FullName));
- var expectedErrorMessages = propertiesCollection.Select(kvp =>
- {
- return string.Format(errorMessageFormat, kvp.Key, kvp.Value);
- });
// Act
var response = await client.PostAsync("http://localhost/Validation/CreateStore", content);
@@ -88,20 +79,12 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
Assert.NotNull(modelBindingInfo.Store.Address);
Assert.Equal(98052, modelBindingInfo.Store.Address.Zipcode);
Assert.Equal("WA", modelBindingInfo.Store.Address.State);
- Assert.NotNull(modelBindingInfo.ModelStateErrorMessages);
- Assert.Equal(expectedErrorMessages.Count(), modelBindingInfo.ModelStateErrorMessages.Count);
- foreach (var expectedErrorMessage in expectedErrorMessages)
- {
- Assert.Contains(
- modelBindingInfo.ModelStateErrorMessages,
- (actualErrorMessage) => actualErrorMessage.Equals(expectedErrorMessage));
- }
+ Assert.Empty(modelBindingInfo.ModelStateErrorMessages);
}
- // Verifies that the model state has errors related to body model validation(for reference types) and also for
- // Required attribute validation (for value types).
+ // Verifies that the model state has errors related to body model validation.
[Fact]
- public async Task DataMissingForReferneceTypeProperties_AndModelIsBound_AndHasMixedValidationErrors()
+ public async Task DataMissingForRefereneceTypeProperties_AndModelIsBound_AndHasMixedValidationErrors()
{
// Arrange
var server = TestHelper.CreateServer(_app, SiteName, _configureServices);
@@ -111,13 +94,8 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
" xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">" +
"10";
var content = new StringContent(input, Encoding.UTF8, "application/xml-dcs");
- var propertiesCollection = new List>();
- propertiesCollection.Add(new KeyValuePair(nameof(Store.Id), typeof(Store).FullName));
- propertiesCollection.Add(new KeyValuePair(nameof(Address.Zipcode), typeof(Address).FullName));
- var expectedErrorMessages = propertiesCollection.Select(kvp =>
- {
- return string.Format(errorMessageFormat, kvp.Key, kvp.Value);
- }).ToList();
+
+ var expectedErrorMessages = new List();
expectedErrorMessages.Add("Address:The Address field is required.");
// Act
@@ -131,6 +109,7 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
Assert.NotNull(modelBindingInfo.Store);
Assert.Equal(10, modelBindingInfo.Store.Id);
Assert.NotNull(modelBindingInfo.ModelStateErrorMessages);
+
Assert.Equal(expectedErrorMessages.Count(), modelBindingInfo.ModelStateErrorMessages.Count);
foreach (var expectedErrorMessage in expectedErrorMessages)
{
diff --git a/test/Microsoft.AspNet.Mvc.Xml.Test/XmlDataContractSerializerInputFormatterTest.cs b/test/Microsoft.AspNet.Mvc.Xml.Test/XmlDataContractSerializerInputFormatterTest.cs
index 0e236923e4..d4584e1b08 100644
--- a/test/Microsoft.AspNet.Mvc.Xml.Test/XmlDataContractSerializerInputFormatterTest.cs
+++ b/test/Microsoft.AspNet.Mvc.Xml.Test/XmlDataContractSerializerInputFormatterTest.cs
@@ -53,15 +53,6 @@ namespace Microsoft.AspNet.Mvc.Xml
public TestLevelOne TestOne { get; set; }
}
- private readonly string requiredErrorMessageFormat = string.Format(
- "{0} does not recognize '{1}', so instead use '{2}' with '{3}' set to '{4}' for value type property " +
- "'{{0}}' on type '{{1}}'.",
- typeof(DataContractSerializer).FullName,
- typeof(RequiredAttribute).FullName,
- typeof(DataMemberAttribute).FullName,
- nameof(DataMemberAttribute.IsRequired),
- bool.TrueString);
-
[Theory]
[InlineData("application/xml", true)]
[InlineData("application/*", true)]
@@ -513,637 +504,6 @@ namespace Microsoft.AspNet.Mvc.Xml
Assert.Equal(expectedInt, dummyModel.SampleInt);
Assert.Equal(expectedString, dummyModel.SampleString);
}
-
- [Fact]
- public async Task PostingListOfModels_HasRequiredAttributeValidationErrors()
- {
- // Arrange
- var input = "" +
- "true98052" +
- "";
- var formatter = new XmlDataContractSerializerInputFormatter();
- var contentBytes = Encodings.UTF8EncodingWithoutBOM.GetBytes(input);
- var context = GetInputFormatterContext(contentBytes, typeof(List));
-
- // Act
- var model = await formatter.ReadAsync(context) as List;
-
- // Assert
- Assert.NotNull(model);
- Assert.Equal(1, model.Count);
- Assert.Equal(98052, model[0].Zipcode);
- Assert.Equal(true, model[0].IsResidential);
-
- Assert.Equal(1, context.ModelState.Keys.Count);
- AssertModelStateErrorMessages(
- typeof(Address).FullName,
- context,
- expectedErrorMessages: new[]
- {
- string.Format(requiredErrorMessageFormat, nameof(Address.Zipcode), typeof(Address).FullName),
- string.Format(
- requiredErrorMessageFormat,
- nameof(Address.IsResidential),
- typeof(Address).FullName)
- });
- }
-
- [Fact]
- public async Task PostingModel_HasRequiredAttributeValidationErrors()
- {
- // Arrange
- var input = "" +
- "" +
- "true98052";
- var formatter = new XmlDataContractSerializerInputFormatter();
- var contentBytes = Encodings.UTF8EncodingWithoutBOM.GetBytes(input);
- var context = GetInputFormatterContext(contentBytes, typeof(Address));
-
- // Act
- var model = await formatter.ReadAsync(context) as Address;
-
- // Assert
- Assert.NotNull(model);
- Assert.Equal(98052, model.Zipcode);
- Assert.Equal(true, model.IsResidential);
-
- Assert.Equal(1, context.ModelState.Keys.Count);
- AssertModelStateErrorMessages(
- typeof(Address).FullName,
- context,
- expectedErrorMessages: new[]
- {
- string.Format(requiredErrorMessageFormat, nameof(Address.Zipcode), typeof(Address).FullName),
- string.Format(
- requiredErrorMessageFormat,
- nameof(Address.IsResidential),
- typeof(Address).FullName)
- });
- }
-
- [Fact]
- public async Task PostingModelWithProperty_HasRequiredAttributeValidationErrors()
- {
- // Arrange
- var input = "" +
- "" +
- "true98052" +
- "";
- var formatter = new XmlDataContractSerializerInputFormatter();
- var contentBytes = Encodings.UTF8EncodingWithoutBOM.GetBytes(input);
- var context = GetInputFormatterContext(
- contentBytes,
- typeof(ModelWithPropertyHavingRequiredAttributeValidationErrors));
-
- // Act
- var model = await formatter.ReadAsync(context) as ModelWithPropertyHavingRequiredAttributeValidationErrors;
-
- // Assert
- Assert.NotNull(model);
- Assert.NotNull(model.AddressProperty);
- Assert.Equal(98052, model.AddressProperty.Zipcode);
- Assert.Equal(true, model.AddressProperty.IsResidential);
-
- Assert.Equal(1, context.ModelState.Keys.Count);
- AssertModelStateErrorMessages(
- typeof(Address).FullName,
- context,
- expectedErrorMessages: new[]
- {
- string.Format(requiredErrorMessageFormat, nameof(Address.Zipcode), typeof(Address).FullName),
- string.Format(requiredErrorMessageFormat, nameof(Address.IsResidential), typeof(Address).FullName)
- });
- }
-
- [Fact]
- public async Task PostingModel_WithCollectionProperty_HasRequiredAttributeValidationErrors()
- {
- // Arrange
- var input = "" +
- "" +
- "true98052" +
- "";
- var formatter = new XmlDataContractSerializerInputFormatter();
- var contentBytes = Encodings.UTF8EncodingWithoutBOM.GetBytes(input);
- var context = GetInputFormatterContext(
- contentBytes,
- typeof(ModelWithCollectionPropertyHavingRequiredAttributeValidationErrors));
-
- // Act
- var model = await formatter.ReadAsync(context)
- as ModelWithCollectionPropertyHavingRequiredAttributeValidationErrors;
-
- // Assert
- Assert.NotNull(model);
- Assert.NotNull(model.Addresses);
- Assert.Equal(98052, model.Addresses[0].Zipcode);
- Assert.Equal(true, model.Addresses[0].IsResidential);
-
- Assert.Equal(1, context.ModelState.Keys.Count);
- AssertModelStateErrorMessages(
- typeof(Address).FullName,
- context,
- new[]
- {
- string.Format(requiredErrorMessageFormat, nameof(Address.Zipcode), typeof(Address).FullName),
- string.Format(requiredErrorMessageFormat, nameof(Address.IsResidential), typeof(Address).FullName)
- });
- }
-
- [Fact]
- public async Task PostingModelInheritingType_HasRequiredAttributeValidationErrors()
- {
- // Arrange
- var input = "" +
- "" +
- "true98052" +
- "";
- var formatter = new XmlDataContractSerializerInputFormatter();
- var contentBytes = Encodings.UTF8EncodingWithoutBOM.GetBytes(input);
- var context = GetInputFormatterContext(
- contentBytes,
- typeof(ModelInheritingTypeHavingRequiredAttributeValidationErrors));
-
- // Act
- var model = await formatter.ReadAsync(context)
- as ModelInheritingTypeHavingRequiredAttributeValidationErrors;
-
- // Assert
- Assert.NotNull(model);
- Assert.Equal(98052, model.Zipcode);
- Assert.Equal(true, model.IsResidential);
-
- Assert.Equal(1, context.ModelState.Keys.Count);
- AssertModelStateErrorMessages(
- typeof(Address).FullName,
- context,
- expectedErrorMessages: new[]
- {
- string.Format(requiredErrorMessageFormat, nameof(Address.Zipcode), typeof(Address).FullName),
- string.Format(requiredErrorMessageFormat, nameof(Address.IsResidential), typeof(Address).FullName)
- });
- }
-
- [Fact]
- public async Task PostingModelHavingNullableValueTypes_NoRequiredAttributeValidationErrors()
- {
- // Arrange
- var input = "" +
- "" +
- "200620072005";
- var formatter = new XmlDataContractSerializerInputFormatter();
- var contentBytes = Encodings.UTF8EncodingWithoutBOM.GetBytes(input);
- var context = GetInputFormatterContext(contentBytes, typeof(CarInfo));
- var expectedModel = new CarInfo() { Year = 2005, ServicedYears = new List() };
- expectedModel.ServicedYears.Add(2006);
- expectedModel.ServicedYears.Add(2007);
-
- // Act
- var model = await formatter.ReadAsync(context) as CarInfo;
-
- // Assert
- Assert.NotNull(model);
- Assert.Equal(expectedModel.Year, model.Year);
- Assert.Equal(expectedModel.ServicedYears, model.ServicedYears);
- Assert.Empty(context.ModelState);
- }
-
- [Fact]
- public async Task PostingModel_WithPropertyHavingNullableValueTypes_NoRequiredAttributeValidationErrors()
- {
- // Arrange
- var input = "" +
- "" +
- "2006" +
- "20072005" +
- "";
- var formatter = new XmlDataContractSerializerInputFormatter();
- var contentBytes = Encodings.UTF8EncodingWithoutBOM.GetBytes(input);
- var context = GetInputFormatterContext(
- contentBytes,
- typeof(ModelWithPropertyHavingTypeWithNullableProperties));
- var expectedModel = new ModelWithPropertyHavingTypeWithNullableProperties()
- {
- CarInfoProperty = new CarInfo() { Year = 2005, ServicedYears = new List() }
- };
-
- expectedModel.CarInfoProperty.ServicedYears.Add(2006);
- expectedModel.CarInfoProperty.ServicedYears.Add(2007);
-
- // Act
- var model = await formatter.ReadAsync(context) as ModelWithPropertyHavingTypeWithNullableProperties;
-
- // Assert
- Assert.NotNull(model);
- Assert.NotNull(model.CarInfoProperty);
- Assert.Equal(expectedModel.CarInfoProperty.Year, model.CarInfoProperty.Year);
- Assert.Equal(expectedModel.CarInfoProperty.ServicedYears, model.CarInfoProperty.ServicedYears);
- Assert.Empty(context.ModelState);
- }
-
- [Fact]
- public async Task PostingModel_WithPropertySelfReferencingItself()
- {
- // Arrange
- var input = "1011MikeJohn";
- var formatter = new XmlDataContractSerializerInputFormatter();
- var contentBytes = Encodings.UTF8EncodingWithoutBOM.GetBytes(input);
- var context = GetInputFormatterContext(contentBytes, typeof(Employee));
- var expectedModel = new Employee()
- {
- Id = 10,
- Name = "John",
- Manager = new Employee()
- {
- Id = 11,
- Name = "Mike"
- }
- };
-
- // Act
- var model = await formatter.ReadAsync(context) as Employee;
-
- // Assert
- Assert.NotNull(model);
- Assert.Equal(expectedModel.Id, model.Id);
- Assert.Equal(expectedModel.Name, model.Name);
- Assert.NotNull(model.Manager);
- Assert.Equal(expectedModel.Manager.Id, model.Manager.Id);
- Assert.Equal(expectedModel.Manager.Name, model.Manager.Name);
- Assert.Null(model.Manager.Manager);
-
- Assert.Equal(1, context.ModelState.Keys.Count);
- AssertModelStateErrorMessages(
- typeof(Employee).FullName,
- context,
- expectedErrorMessages: new[]
- {
- string.Format(requiredErrorMessageFormat, nameof(Employee.Id), typeof(Employee).FullName)
- });
- }
-
- [Fact]
- public async Task PostingModel_WithBothRequiredAndDataMemberRequired_NoValidationErrors()
- {
- // Arrange
- var input = "" +
- "" +
- "10true";
- var formatter = new XmlDataContractSerializerInputFormatter();
- var contentBytes = Encodings.UTF8EncodingWithoutBOM.GetBytes(input);
- var context = GetInputFormatterContext(contentBytes, typeof(Laptop));
-
- // Act
- var model = await formatter.ReadAsync(context) as Laptop;
-
- // Assert
- Assert.NotNull(model);
- Assert.Equal(10, model.Id);
- Assert.Equal(true, model.SupportsVirtualization);
- Assert.Empty(context.ModelState);
- }
-
- [Fact]
- public async Task PostingListofModels_WithBothRequiredAndDataMemberRequired_NoValidationErrors()
- {
- // Arrange
- var input = "" +
- "" +
- "10true";
- var formatter = new XmlDataContractSerializerInputFormatter();
- var contentBytes = Encodings.UTF8EncodingWithoutBOM.GetBytes(input);
- var context = GetInputFormatterContext(contentBytes, typeof(List));
-
- // Act
- var model = await formatter.ReadAsync(context) as List;
-
- // Assert
- Assert.NotNull(model);
- Assert.Equal(1, model.Count);
- Assert.Equal(10, model[0].Id);
- Assert.Equal(true, model[0].SupportsVirtualization);
- Assert.Empty(context.ModelState);
- }
-
- [Fact]
- public async Task PostingModel_WithRequiredAndDataMemberNoRequired_HasValidationErrors()
- {
- // Arrange
- var input = "" +
- "" +
- "10Phone";
- var formatter = new XmlDataContractSerializerInputFormatter();
- var contentBytes = Encodings.UTF8EncodingWithoutBOM.GetBytes(input);
- var context = GetInputFormatterContext(contentBytes, typeof(Product));
-
- // Act
- var model = await formatter.ReadAsync(context) as Product;
-
- // Assert
- Assert.NotNull(model);
- Assert.Equal(10, model.Id);
-
- Assert.Equal(2, context.ModelState.Keys.Count);
- AssertModelStateErrorMessages(
- typeof(Product).FullName,
- context,
- expectedErrorMessages: new[]
- {
- string.Format(requiredErrorMessageFormat, nameof(Product.Id), typeof(Product).FullName)
- });
-
- AssertModelStateErrorMessages(
- typeof(Address).FullName,
- context,
- expectedErrorMessages: new[]
- {
- string.Format(requiredErrorMessageFormat, nameof(Address.Zipcode), typeof(Address).FullName),
- string.Format(requiredErrorMessageFormat, nameof(Address.IsResidential), typeof(Address).FullName)
- });
- }
-
- [Fact]
- public async Task PostingListOfModels_WithRequiredAndDataMemberNoRequired_HasValidationErrors()
- {
- // Arrange
- var input = "" +
- "" +
- "10Phone";
- var formatter = new XmlDataContractSerializerInputFormatter();
- var contentBytes = Encodings.UTF8EncodingWithoutBOM.GetBytes(input);
- var context = GetInputFormatterContext(contentBytes, typeof(List));
-
- // Act
- var model = await formatter.ReadAsync(context) as List;
-
- // Assert
- Assert.NotNull(model);
- Assert.Equal(1, model.Count);
- Assert.Equal(10, model[0].Id);
- Assert.Equal("Phone", model[0].Name);
-
- Assert.Equal(2, context.ModelState.Keys.Count);
- AssertModelStateErrorMessages(
- typeof(Product).FullName,
- context,
- expectedErrorMessages: new[]
- {
- string.Format(requiredErrorMessageFormat, nameof(Product.Id), typeof(Product).FullName)
- });
-
- AssertModelStateErrorMessages(
- typeof(Address).FullName,
- context,
- expectedErrorMessages: new[]
- {
- string.Format(requiredErrorMessageFormat, nameof(Address.Zipcode), typeof(Address).FullName),
- string.Format(requiredErrorMessageFormat, nameof(Address.IsResidential), typeof(Address).FullName)
- });
- }
-
- [Fact]
- public async Task PostingModel_WithDeeperHierarchy_HasValidationErrors()
- {
- // Arrange
- var input = "" +
- "";
- var formatter = new XmlDataContractSerializerInputFormatter();
- var contentBytes = Encodings.UTF8EncodingWithoutBOM.GetBytes(input);
- var context = GetInputFormatterContext(contentBytes, typeof(Store));
-
- // Act
- var model = await formatter.ReadAsync(context) as Store;
-
- // Assert
- Assert.Null(model);
-
- Assert.Equal(3, context.ModelState.Keys.Count);
- AssertModelStateErrorMessages(
- typeof(Address).FullName,
- context,
- expectedErrorMessages: new[]
- {
- string.Format(requiredErrorMessageFormat, nameof(Address.IsResidential), typeof(Address).FullName),
- string.Format(requiredErrorMessageFormat, nameof(Address.Zipcode), typeof(Address).FullName)
- });
-
- AssertModelStateErrorMessages(
- typeof(Employee).FullName,
- context,
- expectedErrorMessages: new[]
- {
- string.Format(requiredErrorMessageFormat, nameof(Employee.Id), typeof(Employee).FullName)
- });
-
- AssertModelStateErrorMessages(
- typeof(Product).FullName,
- context,
- expectedErrorMessages: new[]
- {
- string.Format(requiredErrorMessageFormat, nameof(Product.Id), typeof(Product).FullName)
- });
- }
-
- [Fact]
- public async Task PostingModelOfStructs_WithDeeperHierarchy_HasValidationErrors()
- {
- // Arrange
- var input = "";
-
- var formatter = new XmlDataContractSerializerInputFormatter();
- var contentBytes = Encodings.UTF8EncodingWithoutBOM.GetBytes(input);
- var context = GetInputFormatterContext(contentBytes, typeof(School));
-
- // Act
- var model = await formatter.ReadAsync(context);
-
- // Assert
- Assert.Null(model);
-
- Assert.Equal(3, context.ModelState.Keys.Count);
- AssertModelStateErrorMessages(
- typeof(School).FullName,
- context,
- expectedErrorMessages: new[]
- {
- string.Format(requiredErrorMessageFormat, nameof(School.Id), typeof(School).FullName)
- });
- AssertModelStateErrorMessages(
- typeof(Website).FullName,
- context,
- expectedErrorMessages: new[]
- {
- string.Format(requiredErrorMessageFormat, nameof(Website.Id), typeof(Website).FullName)
- });
-
- AssertModelStateErrorMessages(
- typeof(Student).FullName,
- context,
- expectedErrorMessages: new[]
- {
- string.Format(requiredErrorMessageFormat, nameof(Student.Id), typeof(Student).FullName)
- });
- }
-
- [Fact]
- public async Task PostingModel_WithDictionaryProperty_HasValidationErrorsOnKeyAndValue()
- {
- // Arrange
- var input = "";
-
- var formatter = new XmlDataContractSerializerInputFormatter();
- var contentBytes = Encodings.UTF8EncodingWithoutBOM.GetBytes(input);
- var context = GetInputFormatterContext(contentBytes, typeof(FavoriteLocations));
-
- // Act
- var model = await formatter.ReadAsync(context);
-
- // Assert
- Assert.Null(model);
-
- Assert.Equal(2, context.ModelState.Keys.Count);
- AssertModelStateErrorMessages(
- typeof(Point).FullName,
- context,
- expectedErrorMessages: new[]
- {
- string.Format(requiredErrorMessageFormat, nameof(Point.X), typeof(Point).FullName),
- string.Format(requiredErrorMessageFormat, nameof(Point.Y), typeof(Point).FullName)
- });
- AssertModelStateErrorMessages(
- typeof(Address).FullName,
- context,
- expectedErrorMessages: new[]
- {
- string.Format(requiredErrorMessageFormat, nameof(Address.IsResidential), typeof(Address).FullName),
- string.Format(requiredErrorMessageFormat, nameof(Address.Zipcode), typeof(Address).FullName)
- });
- }
-
- [Fact]
- public async Task PostingModel_WithDifferentValueTypeProperties_HasValidationErrors()
- {
- // Arrange
- var input = "";
-
- var formatter = new XmlDataContractSerializerInputFormatter();
- var contentBytes = Encodings.UTF8EncodingWithoutBOM.GetBytes(input);
- var context = GetInputFormatterContext(contentBytes, typeof(ValueTypePropertiesModel));
-
- // Act
- var model = await formatter.ReadAsync(context);
-
- // Assert
- Assert.Null(model);
-
- Assert.Equal(3, context.ModelState.Keys.Count);
- AssertModelStateErrorMessages(
- typeof(Point).FullName,
- context,
- expectedErrorMessages: new[]
- {
- string.Format(requiredErrorMessageFormat, nameof(Point.X), typeof(Point).FullName),
- string.Format(requiredErrorMessageFormat, nameof(Point.X), typeof(Point).FullName)
- });
- AssertModelStateErrorMessages(
- typeof(GpsCoordinate).FullName,
- context,
- expectedErrorMessages: new[]
- {
- string.Format(
- requiredErrorMessageFormat,
- nameof(GpsCoordinate.Latitude),
- typeof(GpsCoordinate).FullName),
- string.Format(
- requiredErrorMessageFormat,
- nameof(GpsCoordinate.Longitude),
- typeof(GpsCoordinate).FullName)
- });
- AssertModelStateErrorMessages(
- typeof(ValueTypePropertiesModel).FullName,
- context,
- expectedErrorMessages: new[]
- {
- string.Format(
- requiredErrorMessageFormat,
- nameof(ValueTypePropertiesModel.IntProperty),
- typeof(ValueTypePropertiesModel).FullName),
- string.Format(
- requiredErrorMessageFormat,
- nameof(ValueTypePropertiesModel.DateTimeProperty),
- typeof(ValueTypePropertiesModel).FullName),
- string.Format(
- requiredErrorMessageFormat,
- nameof(ValueTypePropertiesModel.PointProperty),
- typeof(ValueTypePropertiesModel).FullName),
- string.Format(
- requiredErrorMessageFormat,
- nameof(ValueTypePropertiesModel.GpsCoordinateProperty),
- typeof(ValueTypePropertiesModel).FullName)
- });
- }
-
- private void AssertModelStateErrorMessages(
- string modelStateKey,
- InputFormatterContext context,
- IEnumerable expectedErrorMessages)
- {
- ModelState modelState;
- context.ModelState.TryGetValue(modelStateKey, out modelState);
-
- Assert.NotNull(modelState);
- Assert.NotEmpty(modelState.Errors);
-
- var actualErrorMessages = modelState.Errors.Select(error =>
- {
- if (string.IsNullOrEmpty(error.ErrorMessage))
- {
- if (error.Exception != null)
- {
- return error.Exception.Message;
- }
- }
-
- return error.ErrorMessage;
- });
-
- Assert.Equal(expectedErrorMessages.Count(), actualErrorMessages.Count());
-
- if (expectedErrorMessages != null)
- {
- foreach (var expectedErrorMessage in expectedErrorMessages)
- {
- Assert.Contains(expectedErrorMessage, actualErrorMessages);
- }
- }
- }
-
private InputFormatterContext GetInputFormatterContext(byte[] contentBytes, Type modelType)
{
var httpContext = GetHttpContext(contentBytes);
@@ -1177,178 +537,4 @@ namespace Microsoft.AspNet.Mvc.Xml
}
}
}
-
- public class Address
- {
- [Required]
- public int Zipcode { get; set; }
-
- [Required]
- public bool IsResidential { get; set; }
- }
-
- public class CarInfo
- {
- [Required]
- public int? Year { get; set; }
-
- [Required]
- public List ServicedYears { get; set; }
- }
-
- public class Employee
- {
- [Required]
- public int Id { get; set; }
-
- [Required]
- public string Name { get; set; }
-
- [Required]
- public Employee Manager { get; set; }
- }
-
- [DataContract]
- public class Laptop
- {
- [DataMember(IsRequired = true)]
- [Required]
- public int Id { get; set; }
-
- [DataMember(IsRequired = true)]
- [Required]
- public bool SupportsVirtualization { get; set; }
- }
-
- [DataContract]
- public class Product
- {
- // Here the property has DataMember but does not set the value 'IsRequired = true'
- [DataMember(Name = "Id")]
- [Required]
- public int Id { get; set; }
-
- [DataMember(Name = "Name")]
- [Required]
- public string Name { get; set; }
-
- [DataMember(Name = "Manufacturer")]
- [Required]
- public Manufacturer Manufacturer { get; set; }
- }
-
- public class ModelWithPropertyHavingRequiredAttributeValidationErrors
- {
- public Address AddressProperty { get; set; }
- }
-
- public class ModelWithCollectionPropertyHavingRequiredAttributeValidationErrors
- {
- public List Addresses { get; set; }
- }
-
- public class ModelInheritingTypeHavingRequiredAttributeValidationErrors : Address
- {
- }
-
- public class ModelWithPropertyHavingTypeWithNullableProperties
- {
- public CarInfo CarInfoProperty { get; set; }
- }
-
- public class Store
- {
- public StoreDetails StoreDetails { get; set; }
-
- public List Products { get; set; }
- }
-
- public class StoreDetails
- {
- public List Employees { get; set; }
-
- public Address Address { get; set; }
- }
-
- public class Manufacturer
- {
- public Address Address { get; set; }
- }
-
- public struct School
- {
- [Required]
- public int Id { get; set; }
-
- public List Students { get; set; }
-
- public Website Address { get; set; }
- }
-
- public struct Student
- {
- [Required]
- public int Id { get; set; }
-
- public Website Address { get; set; }
- }
-
- public struct Website
- {
- [Required]
- public int Id { get; set; }
-
- [Required]
- public string Name { get; set; }
- }
-
- public struct ValueTypePropertiesModel
- {
- [Required]
- public int IntProperty { get; set; }
-
- [Required]
- public int? NullableIntProperty { get; set; }
-
- [Required]
- public DateTime DateTimeProperty { get; set; }
-
- [Required]
- public DateTime? NullableDateTimeProperty { get; set; }
-
- [Required]
- public Point PointProperty { get; set; }
-
- [Required]
- public Point? NullablePointProperty { get; set; }
-
- [Required]
- public GpsCoordinate GpsCoordinateProperty { get; set; }
-
- [Required]
- public GpsCoordinate? NullableGpsCoordinateProperty { get; set; }
- }
-
- public struct GpsCoordinate
- {
- [Required]
- public Point Latitude { get; set; }
-
- [Required]
- public Point Longitude { get; set; }
- }
-
- public struct Point
- {
- [Required]
- public int X { get; set; }
-
- [Required]
- public int Y { get; set; }
- }
-
- public class FavoriteLocations
- {
- public Dictionary Addresses { get; set; }
- }
}
\ No newline at end of file