diff --git a/src/Microsoft.AspNet.Mvc.Core/ActionResults/JsonResult.cs b/src/Microsoft.AspNet.Mvc.Core/ActionResults/JsonResult.cs index 3318b2c5d6..faa96e6cc9 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ActionResults/JsonResult.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ActionResults/JsonResult.cs @@ -43,7 +43,7 @@ namespace Microsoft.AspNet.Mvc /// The to be used by /// the formatter. public JsonResult(object value, [NotNull] JsonSerializerSettings serializerSettings) - : this(value, formatter: new JsonOutputFormatter { SerializerSettings = serializerSettings }) + : this(value, formatter: new JsonOutputFormatter(serializerSettings)) { } diff --git a/src/Microsoft.AspNet.Mvc.Core/Formatters/JsonContractResolver.cs b/src/Microsoft.AspNet.Mvc.Core/Formatters/JsonContractResolver.cs deleted file mode 100644 index d46c85f265..0000000000 --- a/src/Microsoft.AspNet.Mvc.Core/Formatters/JsonContractResolver.cs +++ /dev/null @@ -1,43 +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.ComponentModel.DataAnnotations; -using System.Reflection; -using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; - -namespace Microsoft.AspNet.Mvc -{ - /// - /// The default for . - /// It determines if a value type member has and sets the appropriate - /// JsonProperty settings. - /// - public class JsonContractResolver : DefaultContractResolver - { - protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) - { - var property = base.CreateProperty(member, memberSerialization); - - var required = member.GetCustomAttribute(typeof(RequiredAttribute), inherit: true); - if (required != null) - { - var propertyType = ((PropertyInfo)member).PropertyType; - - // DefaultObjectValidator does required attribute validation on properties based on the property - // value being null. Since this is not possible in case of value types, we depend on the formatters - // to handle value type validation. - // With the following settings here, if a value is not present on the wire for value types - // like primitive, struct etc., Json.net's serializer would throw exception which we catch - // and add it to model state. - if (propertyType.GetTypeInfo().IsValueType && !TypeHelper.IsNullableValueType(propertyType)) - { - property.Required = Required.AllowNull; - } - } - - return property; - } - } -} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Core/Formatters/JsonInputFormatter.cs b/src/Microsoft.AspNet.Mvc.Core/Formatters/JsonInputFormatter.cs index 0d56574795..3498331fef 100644 --- a/src/Microsoft.AspNet.Mvc.Core/Formatters/JsonInputFormatter.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Formatters/JsonInputFormatter.cs @@ -5,6 +5,7 @@ using System; using System.IO; using System.Text; using System.Threading.Tasks; +using Microsoft.AspNet.Mvc.Core.Internal; using Microsoft.Framework.Internal; using Microsoft.Net.Http.Headers; using Newtonsoft.Json; @@ -13,31 +14,22 @@ namespace Microsoft.AspNet.Mvc { public class JsonInputFormatter : InputFormatter { - private const int DefaultMaxDepth = 32; - private JsonSerializerSettings _jsonSerializerSettings; + private JsonSerializerSettings _serializerSettings; public JsonInputFormatter() + : this(SerializerSettingsProvider.CreateSerializerSettings()) { + } + + public JsonInputFormatter([NotNull] JsonSerializerSettings serializerSettings) + { + _serializerSettings = serializerSettings; + SupportedEncodings.Add(Encodings.UTF8EncodingWithoutBOM); SupportedEncodings.Add(Encodings.UTF16EncodingLittleEndian); SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("application/json")); SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("text/json")); - - _jsonSerializerSettings = new JsonSerializerSettings - { - MissingMemberHandling = MissingMemberHandling.Ignore, - - // Limit the object graph we'll consume to a fixed depth. This prevents stackoverflow exceptions - // from deserialization errors that might occur from deeply nested objects. - MaxDepth = DefaultMaxDepth, - - // Do not change this setting - // Setting this to None prevents Json.NET from loading malicious, unsafe, or security-sensitive types - TypeNameHandling = TypeNameHandling.None - }; - - _jsonSerializerSettings.ContractResolver = new JsonContractResolver(); } /// @@ -45,15 +37,14 @@ namespace Microsoft.AspNet.Mvc /// public JsonSerializerSettings SerializerSettings { - get { return _jsonSerializerSettings; } + get + { + return _serializerSettings; + } + [param: NotNull] set { - if (value == null) - { - throw new ArgumentNullException(nameof(value)); - } - - _jsonSerializerSettings = value; + _serializerSettings = value; } } diff --git a/src/Microsoft.AspNet.Mvc.Core/Formatters/JsonOutputFormatter.cs b/src/Microsoft.AspNet.Mvc.Core/Formatters/JsonOutputFormatter.cs index 1b4ab69cf9..e6f9a47bd9 100644 --- a/src/Microsoft.AspNet.Mvc.Core/Formatters/JsonOutputFormatter.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Formatters/JsonOutputFormatter.cs @@ -4,6 +4,7 @@ using System; using System.IO; using System.Threading.Tasks; +using Microsoft.AspNet.Mvc.Core.Internal; using Microsoft.AspNet.Mvc.Internal; using Microsoft.Framework.Internal; using Microsoft.Net.Http.Headers; @@ -16,13 +17,18 @@ namespace Microsoft.AspNet.Mvc private JsonSerializerSettings _serializerSettings; public JsonOutputFormatter() + : this(SerializerSettingsProvider.CreateSerializerSettings()) { + } + + public JsonOutputFormatter([NotNull] JsonSerializerSettings serializerSettings) + { + _serializerSettings = serializerSettings; + SupportedEncodings.Add(Encodings.UTF8EncodingWithoutBOM); SupportedEncodings.Add(Encodings.UTF16EncodingLittleEndian); SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("application/json")); SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("text/json")); - - _serializerSettings = new JsonSerializerSettings(); } /// @@ -30,14 +36,13 @@ namespace Microsoft.AspNet.Mvc /// public JsonSerializerSettings SerializerSettings { - get { return _serializerSettings; } + get + { + return _serializerSettings; + } + [param: NotNull] set { - if (value == null) - { - throw new ArgumentNullException(nameof(value)); - } - _serializerSettings = value; } } diff --git a/src/Microsoft.AspNet.Mvc.Core/Formatters/JsonPatchInputFormatter.cs b/src/Microsoft.AspNet.Mvc.Core/Formatters/JsonPatchInputFormatter.cs index 2c5ccdf7ce..bcdd9f5a1a 100644 --- a/src/Microsoft.AspNet.Mvc.Core/Formatters/JsonPatchInputFormatter.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Formatters/JsonPatchInputFormatter.cs @@ -6,12 +6,20 @@ using System.Threading.Tasks; using Microsoft.AspNet.JsonPatch; using Microsoft.Framework.Internal; using Microsoft.Net.Http.Headers; +using Microsoft.AspNet.Mvc.Core.Internal; +using Newtonsoft.Json; namespace Microsoft.AspNet.Mvc { public class JsonPatchInputFormatter : JsonInputFormatter { public JsonPatchInputFormatter() + : this(SerializerSettingsProvider.CreateSerializerSettings()) + { + } + + public JsonPatchInputFormatter([NotNull] JsonSerializerSettings serializerSettings) + : base(serializerSettings) { // Clear all values and only include json-patch+json value. SupportedMediaTypes.Clear(); @@ -23,7 +31,7 @@ namespace Microsoft.AspNet.Mvc public async override Task ReadRequestBodyAsync([NotNull] InputFormatterContext context) { var jsonPatchDocument = (IJsonPatchDocument)(await base.ReadRequestBodyAsync(context)); - if (jsonPatchDocument != null) + if (jsonPatchDocument != null && SerializerSettings.ContractResolver != null) { jsonPatchDocument.ContractResolver = SerializerSettings.ContractResolver; } diff --git a/src/Microsoft.AspNet.Mvc.Core/Internal/SerializerSettingsProvider.cs b/src/Microsoft.AspNet.Mvc.Core/Internal/SerializerSettingsProvider.cs new file mode 100644 index 0000000000..86c416061b --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Core/Internal/SerializerSettingsProvider.cs @@ -0,0 +1,35 @@ +// 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 Newtonsoft.Json; + +namespace Microsoft.AspNet.Mvc.Core.Internal +{ + /// + /// Helper class which provides . + /// + internal static class SerializerSettingsProvider + { + private const int DefaultMaxDepth = 32; + + /// + /// Creates default . + /// + /// Default . + public static JsonSerializerSettings CreateSerializerSettings() + { + return new JsonSerializerSettings + { + MissingMemberHandling = MissingMemberHandling.Ignore, + + // Limit the object graph we'll consume to a fixed depth. This prevents stackoverflow exceptions + // from deserialization errors that might occur from deeply nested objects. + MaxDepth = DefaultMaxDepth, + + // Do not change this setting + // Setting this to None prevents Json.NET from loading malicious, unsafe, or security-sensitive types + TypeNameHandling = TypeNameHandling.None + }; + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Core/MvcOptions.cs b/src/Microsoft.AspNet.Mvc.Core/MvcOptions.cs index 58cfdae65f..4f831f9d85 100644 --- a/src/Microsoft.AspNet.Mvc.Core/MvcOptions.cs +++ b/src/Microsoft.AspNet.Mvc.Core/MvcOptions.cs @@ -5,9 +5,11 @@ using System; using System.Collections.Generic; using Microsoft.AspNet.Mvc.ApplicationModels; using Microsoft.AspNet.Mvc.Core; +using Microsoft.AspNet.Mvc.Core.Internal; using Microsoft.AspNet.Mvc.ModelBinding; using Microsoft.AspNet.Mvc.ModelBinding.Metadata; using Microsoft.AspNet.Mvc.ModelBinding.Validation; +using Newtonsoft.Json; namespace Microsoft.AspNet.Mvc { @@ -34,6 +36,7 @@ namespace Microsoft.AspNet.Mvc ModelValidatorProviders = new List(); ClientModelValidatorProviders = new List(); CacheProfiles = new Dictionary(StringComparer.OrdinalIgnoreCase); + SerializerSettings = SerializerSettingsProvider.CreateSerializerSettings(); } /// @@ -81,6 +84,11 @@ namespace Microsoft.AspNet.Mvc /// public IList InputFormatters { get; } + /// + /// Gets the that are used by this application. + /// + public JsonSerializerSettings SerializerSettings { get; } + /// /// Gets a list of s that are used by this application. /// diff --git a/src/Microsoft.AspNet.Mvc.Core/Rendering/JsonHelper.cs b/src/Microsoft.AspNet.Mvc.Core/Rendering/JsonHelper.cs index 789b4783c8..ba84f76e5d 100644 --- a/src/Microsoft.AspNet.Mvc.Core/Rendering/JsonHelper.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Rendering/JsonHelper.cs @@ -34,10 +34,7 @@ namespace Microsoft.AspNet.Mvc.Rendering /// public HtmlString Serialize(object value, [NotNull] JsonSerializerSettings serializerSettings) { - var jsonOutputFormatter = new JsonOutputFormatter - { - SerializerSettings = serializerSettings - }; + var jsonOutputFormatter = new JsonOutputFormatter(serializerSettings); return SerializeInternal(jsonOutputFormatter, value); } diff --git a/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ApiController.cs b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ApiController.cs index b3d9cf2f8e..4e00bdb9f2 100644 --- a/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ApiController.cs +++ b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ApiController.cs @@ -257,10 +257,7 @@ namespace System.Web.Http [NonAction] public virtual JsonResult Json([NotNull] T content, [NotNull] JsonSerializerSettings serializerSettings) { - var formatter = new JsonOutputFormatter() - { - SerializerSettings = serializerSettings, - }; + var formatter = new JsonOutputFormatter(serializerSettings); return new JsonResult(content, formatter); } @@ -279,10 +276,7 @@ namespace System.Web.Http [NotNull] JsonSerializerSettings serializerSettings, [NotNull] Encoding encoding) { - var formatter = new JsonOutputFormatter() - { - SerializerSettings = serializerSettings, - }; + var formatter = new JsonOutputFormatter(serializerSettings); formatter.SupportedEncodings.Clear(); formatter.SupportedEncodings.Add(encoding); diff --git a/src/Microsoft.AspNet.Mvc/MvcOptionsSetup.cs b/src/Microsoft.AspNet.Mvc/MvcOptionsSetup.cs index 1bfcbd0852..f75df92f12 100644 --- a/src/Microsoft.AspNet.Mvc/MvcOptionsSetup.cs +++ b/src/Microsoft.AspNet.Mvc/MvcOptionsSetup.cs @@ -48,14 +48,14 @@ namespace Microsoft.AspNet.Mvc options.OutputFormatters.Add(new HttpNoContentOutputFormatter()); options.OutputFormatters.Add(new StringOutputFormatter()); options.OutputFormatters.Add(new StreamOutputFormatter()); - options.OutputFormatters.Add(new JsonOutputFormatter()); + options.OutputFormatters.Add(new JsonOutputFormatter(options.SerializerSettings)); // Set up default mapping for json extensions to content type options.FormatterMappings.SetMediaTypeMappingForFormat("json", MediaTypeHeaderValue.Parse("application/json")); // Set up default input formatters. - options.InputFormatters.Add(new JsonInputFormatter()); - options.InputFormatters.Add(new JsonPatchInputFormatter()); + options.InputFormatters.Add(new JsonInputFormatter(options.SerializerSettings)); + options.InputFormatters.Add(new JsonPatchInputFormatter(options.SerializerSettings)); // Set up ValueProviders options.ValueProviderFactories.Add(new RouteValueValueProviderFactory()); diff --git a/src/Microsoft.AspNet.Mvc/MvcServiceCollectionExtensions.cs b/src/Microsoft.AspNet.Mvc/MvcServiceCollectionExtensions.cs index 0091994a11..37bb13f21b 100644 --- a/src/Microsoft.AspNet.Mvc/MvcServiceCollectionExtensions.cs +++ b/src/Microsoft.AspNet.Mvc/MvcServiceCollectionExtensions.cs @@ -102,7 +102,12 @@ namespace Microsoft.Framework.DependencyInjection return new DefaultCompositeMetadataDetailsProvider(options.ModelMetadataDetailsProviders); })); - services.TryAdd(ServiceDescriptor.Instance(typeof(JsonOutputFormatter), new JsonOutputFormatter())); + // JsonOutputFormatter should use the SerializerSettings on MvcOptions + services.TryAdd(ServiceDescriptor.Singleton(serviceProvider => + { + var options = serviceProvider.GetRequiredService>().Options; + return new JsonOutputFormatter(options.SerializerSettings); + })); // Razor, Views and runtime compilation diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/Formatters/JsonInputFormatterTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/Formatters/JsonInputFormatterTest.cs index ee8d39caba..61ddfe47c5 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/Formatters/JsonInputFormatterTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/Formatters/JsonInputFormatterTest.cs @@ -166,6 +166,18 @@ namespace Microsoft.AspNet.Mvc Assert.NotNull(jsonFormatter.SerializerSettings); } + [Fact] + public void Constructor_UsesSerializerSettings() + { + // Arrange + // Act + var serializerSettings = new JsonSerializerSettings(); + var jsonFormatter = new JsonInputFormatter(serializerSettings); + + // Assert + Assert.Same(serializerSettings, jsonFormatter.SerializerSettings); + } + [Fact] public async Task ChangesTo_DefaultSerializerSettings_TakesEffect() { @@ -219,83 +231,6 @@ namespace Microsoft.AspNet.Mvc Assert.Contains("Required property 'Password' not found in JSON", modelErrorMessage); } - [Fact] - public async Task ThrowsException_OnSupplyingNull_ForRequiredValueType() - { - // Arrange - var contentBytes = Encoding.UTF8.GetBytes("{\"Id\":\"null\",\"Name\":\"Programming C#\"}"); - var jsonFormatter = new JsonInputFormatter(); - var actionContext = GetActionContext(contentBytes, "application/json;charset=utf-8"); - var metadata = new EmptyModelMetadataProvider().GetMetadataForType(typeof(Book)); - var inputFormatterContext = new InputFormatterContext(actionContext, metadata.ModelType); - - // Act - var obj = await jsonFormatter.ReadAsync(inputFormatterContext); - - // Assert - var book = obj as Book; - Assert.NotNull(book); - Assert.Equal(0, book.Id); - Assert.Equal("Programming C#", book.Name); - Assert.False(actionContext.ModelState.IsValid); - - Assert.Equal(1, actionContext.ModelState.Values.First().Errors.Count); - var modelErrorMessage = actionContext.ModelState.Values.First().Errors[0].Exception.Message; - Assert.Contains("Could not convert string to integer: null. Path 'Id'", modelErrorMessage); - } - - [Theory] - [InlineData(typeof(Book))] - [InlineData(typeof(EBook))] - public async Task Validates_RequiredAttribute_OnRegularAndInheritedProperties(Type type) - { - // Arrange - var contentBytes = Encoding.UTF8.GetBytes("{ \"Name\" : \"Programming C#\"}"); - var jsonFormatter = new JsonInputFormatter(); - var actionContext = GetActionContext(contentBytes, "application/json;charset=utf-8"); - var metadata = new EmptyModelMetadataProvider().GetMetadataForType(type); - var inputFormatterContext = new InputFormatterContext(actionContext, metadata.ModelType); - - // Act - var obj = await jsonFormatter.ReadAsync(inputFormatterContext); - - // Assert - Assert.False(actionContext.ModelState.IsValid); - Assert.Equal(1, actionContext.ModelState.Count); - - var modelErrorMessage = actionContext.ModelState.Values.First().Errors[0].Exception.Message; - Assert.Contains("Required property 'Id' not found in JSON", modelErrorMessage); - } - - [Fact] - public async Task Validates_RequiredAttributeOnStructTypes() - { - // Arrange - var contentBytes = Encoding.UTF8.GetBytes("{\"Longitude\":{}}"); - var jsonFormatter = new JsonInputFormatter(); - var actionContext = GetActionContext(contentBytes, "application/json;charset=utf-8"); - var metadata = new EmptyModelMetadataProvider().GetMetadataForType(typeof(GpsCoordinate)); - var inputFormatterContext = new InputFormatterContext(actionContext, metadata.ModelType); - - // Act - var obj = await jsonFormatter.ReadAsync(inputFormatterContext); - - // Assert - Assert.False(actionContext.ModelState.IsValid); - Assert.Equal(2, actionContext.ModelState.Count); - var errorMessages = GetModelStateErrorMessages(actionContext.ModelState); - Assert.Equal(3, errorMessages.Count()); - Assert.Contains( - errorMessages, - (errorMessage) => errorMessage.Contains("Required property 'Latitude' not found in JSON")); - Assert.Contains( - errorMessages, - (errorMessage) => errorMessage.Contains("Required property 'X' not found in JSON")); - Assert.Contains( - errorMessages, - (errorMessage) => errorMessage.Contains("Required property 'Y' not found in JSON")); - } - [Fact] public async Task Validation_DoesNotHappen_ForNonRequired_ValueTypeProperties() { @@ -317,28 +252,6 @@ namespace Microsoft.AspNet.Mvc Assert.Equal("Seattle", location.Name); } - [Fact] - public async Task Validation_DoesNotHappen_OnNullableValueTypeProperties() - { - // Arrange - var contentBytes = Encoding.UTF8.GetBytes("{}"); - var jsonFormatter = new JsonInputFormatter(); - var actionContext = GetActionContext(contentBytes, "application/json;charset=utf-8"); - var metadata = new EmptyModelMetadataProvider().GetMetadataForType(typeof(Venue)); - var inputFormatterContext = new InputFormatterContext(actionContext, metadata.ModelType); - - // Act - var obj = await jsonFormatter.ReadAsync(inputFormatterContext); - - // Assert - Assert.True(actionContext.ModelState.IsValid); - var venue = obj as Venue; - Assert.NotNull(venue); - Assert.Null(venue.Location); - Assert.Null(venue.NearByLocations); - Assert.Null(venue.Name); - } - private static ActionContext GetActionContext(byte[] contentBytes, string contentType = "application/xml") { @@ -407,55 +320,12 @@ namespace Microsoft.AspNet.Mvc public string Password { get; set; } } - private class Book - { - [Required] - public int Id { get; set; } - - [Required] - public string Name { get; set; } - } - - private class EBook : Book - { - } - - private struct Point - { - [Required] - public int X { get; set; } - - [Required] - public int Y { get; set; } - } - - private class GpsCoordinate - { - [Required] - public Point Latitude { get; set; } - - [Required] - public Point Longitude { get; set; } - } - private class Location { public int Id { get; set; } public string Name { get; set; } } - - private class Venue - { - [Required] - public string Name { get; set; } - - [Required] - public Point? Location { get; set; } - - [Required] - public List NearByLocations { get; set; } - } } } #endif diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/Formatters/JsonOutputFormatterTests.cs b/test/Microsoft.AspNet.Mvc.Core.Test/Formatters/JsonOutputFormatterTests.cs index 8ff99b3b3d..6e587a48bc 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/Formatters/JsonOutputFormatterTests.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/Formatters/JsonOutputFormatterTests.cs @@ -30,6 +30,17 @@ namespace Microsoft.AspNet.Mvc.Core.Test.Formatters Assert.NotNull(jsonFormatter.SerializerSettings); } + [Fact] + public void Constructor_UsesSerializerSettings() + { + // Arrange + // Act + var serializerSettings = new JsonSerializerSettings(); + var jsonFormatter = new JsonInputFormatter(serializerSettings); + + // Assert + Assert.Same(serializerSettings, jsonFormatter.SerializerSettings); + } [Fact] public async Task ChangesTo_DefaultSerializerSettings_TakesEffect() diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/JsonPatchTest.cs b/test/Microsoft.AspNet.Mvc.FunctionalTests/JsonPatchTest.cs index 99d7e3aa17..c9e3abd431 100644 --- a/test/Microsoft.AspNet.Mvc.FunctionalTests/JsonPatchTest.cs +++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/JsonPatchTest.cs @@ -133,6 +133,7 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests // Assert var body = await response.Content.ReadAsStringAsync(); + var customer = JsonConvert.DeserializeObject(body); Assert.Equal("Order0", customer.Orders[1].OrderName); Assert.Null(customer.Orders[0].OrderName); diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/ModelBindingTest.cs b/test/Microsoft.AspNet.Mvc.FunctionalTests/ModelBindingTest.cs index b40d752e1a..e781c6d91a 100644 --- a/test/Microsoft.AspNet.Mvc.FunctionalTests/ModelBindingTest.cs +++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/ModelBindingTest.cs @@ -1906,75 +1906,6 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests Assert.Equal("March", user.RegisterationMonth); } - public static TheoryData ModelStateHasErrorsForValueAndReferenceTypesData - { - get - { - return new TheoryData() - { - { - "{}", - new[] - { - ":Required property 'Id' not found in JSON", - "Lines:The Lines field is required." - } - }, - { - "{\"Id\":10}", - new[] - { - "Lines:The Lines field is required." - } - }, - { - "{\"Id\":10,\"Lines\":[{}]}", - new [] - { - "Lines[0]:Required property 'Start' not found in JSON", - "Lines[0]:Required property 'End' not found in JSON" - } - }, - { - "{\"Id\":10,\"Lines\":[{\"Start\":{\"X\":10,\"Y\":10},\"End\":{\"X\":10}}]}", - new [] - { - "Lines[0].End:Required property 'Y' not found in JSON" - } - } - }; - } - } - - [Theory] - [MemberData(nameof(ModelStateHasErrorsForValueAndReferenceTypesData))] - public async Task ModelState_HasErrors_ForValueAndReferenceTypes( - string input, - IEnumerable expectedModelStateErrorMessages) - { - // Arrange - var server = TestHelper.CreateServer(_app, SiteName, _configureServices); - var client = server.CreateClient(); - var content = new StringContent(input, Encoding.UTF8, "text/json"); - - // Act - var response = await client.PostAsync( - "http://localhost/Validation/CreateRectangle", - content); - - // Assert - var data = await response.Content.ReadAsStringAsync(); - var actualModelStateErrorMessages = JsonConvert.DeserializeObject>(data); - Assert.NotNull(actualModelStateErrorMessages); - Assert.Equal(expectedModelStateErrorMessages.Count(), actualModelStateErrorMessages.Count()); - foreach (var expectedErrorMessage in expectedModelStateErrorMessages) - { - Assert.Contains( - actualModelStateErrorMessages, - (actualErrorMessage) => actualErrorMessage.StartsWith(expectedErrorMessage)); - } - } - [Fact] public async Task BindModelAsync_WithCollection() { diff --git a/test/Microsoft.AspNet.Mvc.Test/MvcOptionsSetupTest.cs b/test/Microsoft.AspNet.Mvc.Test/MvcOptionsSetupTest.cs index 9c886ba18a..20645dc032 100644 --- a/test/Microsoft.AspNet.Mvc.Test/MvcOptionsSetupTest.cs +++ b/test/Microsoft.AspNet.Mvc.Test/MvcOptionsSetupTest.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Linq; using System.Xml.Linq; using Microsoft.AspNet.Mvc.ModelBinding; using Microsoft.AspNet.Mvc.ModelBinding.Validation; @@ -207,5 +208,29 @@ namespace Microsoft.AspNet.Mvc Assert.IsType(mvcOptions.ValidationExcludeFilters[i++]); Assert.Equal(xmlNodeFilter.ExcludedTypeName, "System.Xml.XmlNode"); } + + [Fact] + public void Setup_JsonFormattersUseSerializerSettings() + { + // Arrange + var mvcOptions = new MvcOptions(); + var setup = new MvcOptionsSetup(); + + // Act + setup.Configure(mvcOptions); + + // Assert + var jsonInputFormatters = mvcOptions.InputFormatters.OfType(); + foreach (var jsonInputFormatter in jsonInputFormatters) + { + Assert.Same(mvcOptions.SerializerSettings, jsonInputFormatter.SerializerSettings); + } + + var jsonOuputFormatters = mvcOptions.OutputFormatters.OfType(); + foreach (var jsonOuputFormatter in jsonOuputFormatters) + { + Assert.Same(mvcOptions.SerializerSettings, jsonOuputFormatter.SerializerSettings); + } + } } } \ No newline at end of file diff --git a/test/WebSites/ModelBindingWebSite/Controllers/ValidationController.cs b/test/WebSites/ModelBindingWebSite/Controllers/ValidationController.cs index 77ce5d7b49..db32d0bbd1 100644 --- a/test/WebSites/ModelBindingWebSite/Controllers/ValidationController.cs +++ b/test/WebSites/ModelBindingWebSite/Controllers/ValidationController.cs @@ -28,51 +28,6 @@ namespace ModelBindingWebSite.Controllers { return ModelState.IsValid; } - - public IActionResult CreateRectangle([FromBody] Rectangle rectangle) - { - if (!ModelState.IsValid) - { - return new ObjectResult(GetModelStateErrorMessages(ModelState)) { StatusCode = 400 }; - } - - return new ObjectResult(rectangle); - } - - private IEnumerable GetModelStateErrorMessages(ModelStateDictionary modelStateDictionary) - { - var allErrorMessages = new List(); - foreach (var keyModelStatePair in modelStateDictionary) - { - var key = keyModelStatePair.Key; - var errors = keyModelStatePair.Value.Errors; - if (errors != null && errors.Count > 0) - { - string errorMessage = null; - foreach (var modelError in errors) - { - if (string.IsNullOrEmpty(modelError.ErrorMessage)) - { - if (modelError.Exception != null) - { - errorMessage = modelError.Exception.Message; - } - } - else - { - errorMessage = modelError.ErrorMessage; - } - - if (errorMessage != null) - { - allErrorMessages.Add(string.Format("{0}:{1}", key, errorMessage)); - } - } - } - } - - return allErrorMessages; - } } public class SelfishPerson diff --git a/test/WebSites/ModelBindingWebSite/Models/Drawing.cs b/test/WebSites/ModelBindingWebSite/Models/Drawing.cs deleted file mode 100644 index 1401a399da..0000000000 --- a/test/WebSites/ModelBindingWebSite/Models/Drawing.cs +++ /dev/null @@ -1,18 +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.Generic; -using System.ComponentModel.DataAnnotations; - -namespace ModelBindingWebSite -{ - public class Drawing - { - [Required] - public int Id { get; set; } - - [Required] - public List Lines { get; set; } - } -} \ No newline at end of file diff --git a/test/WebSites/ModelBindingWebSite/Models/Rectangle.cs b/test/WebSites/ModelBindingWebSite/Models/Rectangle.cs deleted file mode 100644 index 130673bdbc..0000000000 --- a/test/WebSites/ModelBindingWebSite/Models/Rectangle.cs +++ /dev/null @@ -1,12 +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; - -namespace ModelBindingWebSite -{ - public class Rectangle : Drawing - { - - } -} \ No newline at end of file