From 40b7636b720298a06b9a42017a8bcace34132fad Mon Sep 17 00:00:00 2001 From: Doug Bunting Date: Thu, 22 Oct 2015 09:16:19 -0700 Subject: [PATCH] Use `ModelMetadata.GetDisplayName()` in error message replacing `FormatException` and `OverflowException` - #3227 - much of change is to tests, creating and passing `ModelMetadata` - updated `InputFormatterContext` to make `ModelMetadata` available to `JsonInputFormatter` - walk `ModelMetadata` tree to get information about property with an issue - add missing `null` checks in `ModelStateDictionaryExtensions` --- .../Formatters/InputFormatterContext.cs | 16 ++-- .../ModelBinding/ModelStateDictionary.cs | 24 ++++-- .../ModelBinding/BodyModelBinder.cs | 4 +- .../ModelBinding/ByteArrayModelBinder.cs | 7 +- .../ModelBinding/MutableObjectModelBinder.cs | 2 +- .../ModelBinding/SimpleTypeModelBinder.cs | 7 +- .../JsonInputFormatter.cs | 51 ++++++++++- .../ModelStateDictionaryExtensions.cs | 55 +++++++++++- .../ModelBinding/ModelStateDictionaryTest.cs | 50 +++++++---- .../Formatters/InputFormatterTest.cs | 45 ++++++++-- .../ModelBinding/ByteArrayModelBinderTests.cs | 2 +- .../ModelBinding/SimpleTypeModelBinderTest.cs | 2 +- .../JsonInputFormatterTest.cs | 85 +++++++++++++++++-- .../JsonPatchInputFormatterTest.cs | 20 +++-- ...ataContractSerializerInputFormatterTest.cs | 16 +++- .../XmlSerializerInputFormatterTest.cs | 16 +++- .../ModelBindingTest.cs | 2 +- .../SimpleTypeModelBinderIntegrationTest.cs | 2 +- .../ModelStateDictionaryExtensionsTest.cs | 24 ++++-- .../HtmlHelperValidationSummaryTest.cs | 14 ++- .../HttpErrorTest.cs | 16 +++- .../SerializableErrorController.cs | 2 +- 22 files changed, 376 insertions(+), 86 deletions(-) diff --git a/src/Microsoft.AspNet.Mvc.Abstractions/Formatters/InputFormatterContext.cs b/src/Microsoft.AspNet.Mvc.Abstractions/Formatters/InputFormatterContext.cs index 8314fb041e..ef3d4e8b80 100644 --- a/src/Microsoft.AspNet.Mvc.Abstractions/Formatters/InputFormatterContext.cs +++ b/src/Microsoft.AspNet.Mvc.Abstractions/Formatters/InputFormatterContext.cs @@ -29,7 +29,7 @@ namespace Microsoft.AspNet.Mvc.Formatters HttpContext httpContext, string modelName, ModelStateDictionary modelState, - Type modelType) + ModelMetadata metadata) { if (httpContext == null) { @@ -46,15 +46,16 @@ namespace Microsoft.AspNet.Mvc.Formatters throw new ArgumentNullException(nameof(modelState)); } - if (modelType == null) + if (metadata == null) { - throw new ArgumentNullException(nameof(modelType)); + throw new ArgumentNullException(nameof(metadata)); } HttpContext = httpContext; ModelName = modelName; ModelState = modelState; - ModelType = modelType; + Metadata = metadata; + ModelType = metadata.ModelType; } /// @@ -73,7 +74,12 @@ namespace Microsoft.AspNet.Mvc.Formatters public ModelStateDictionary ModelState { get; } /// - /// Gets the expected of the model represented by the request body. + /// Gets the requested of the request body deserialization. + /// + public ModelMetadata Metadata { get; } + + /// + /// Gets the requested of the request body deserialization. /// public Type ModelType { get; } } diff --git a/src/Microsoft.AspNet.Mvc.Abstractions/ModelBinding/ModelStateDictionary.cs b/src/Microsoft.AspNet.Mvc.Abstractions/ModelBinding/ModelStateDictionary.cs index 8245161971..bb27c24dff 100644 --- a/src/Microsoft.AspNet.Mvc.Abstractions/ModelBinding/ModelStateDictionary.cs +++ b/src/Microsoft.AspNet.Mvc.Abstractions/ModelBinding/ModelStateDictionary.cs @@ -69,7 +69,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding /// /// /// tracks the number of model errors added by calls to - /// or . + /// or + /// . /// Once the value of MaxAllowedErrors - 1 is reached, if another attempt is made to add an error, /// the error message will be ignored and a will be added. /// @@ -202,7 +203,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding /// /// The key of the to add errors to. /// The to add. - public void AddModelError(string key, Exception exception) + public void AddModelError(string key, Exception exception, ModelMetadata metadata) { if (key == null) { @@ -214,7 +215,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding throw new ArgumentNullException(nameof(exception)); } - TryAddModelError(key, exception); + if (metadata == null) + { + throw new ArgumentNullException(nameof(metadata)); + } + + TryAddModelError(key, exception, metadata); } /// @@ -228,7 +234,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding /// True if the given error was added, false if the error was ignored. /// See . /// - public bool TryAddModelError(string key, Exception exception) + public bool TryAddModelError(string key, Exception exception, ModelMetadata metadata) { if (key == null) { @@ -240,6 +246,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding throw new ArgumentNullException(nameof(exception)); } + if (metadata == null) + { + throw new ArgumentNullException(nameof(metadata)); + } + if (ErrorCount >= MaxAllowedErrors - 1) { EnsureMaxErrorsReachedRecorded(); @@ -252,16 +263,17 @@ namespace Microsoft.AspNet.Mvc.ModelBinding ModelStateEntry entry; TryGetValue(key, out entry); + var name = metadata.GetDisplayName(); string errorMessage; if (entry == null) { - errorMessage = Resources.FormatModelError_InvalidValue_GenericMessage(key); + errorMessage = Resources.FormatModelError_InvalidValue_GenericMessage(name); } else { errorMessage = Resources.FormatModelError_InvalidValue_MessageWithModelValue( entry.AttemptedValue, - key); + name); } return TryAddModelError(key, errorMessage); diff --git a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/BodyModelBinder.cs b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/BodyModelBinder.cs index b62726ddd8..739410f6bc 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/BodyModelBinder.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/BodyModelBinder.cs @@ -59,7 +59,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding httpContext, modelBindingKey, bindingContext.ModelState, - bindingContext.ModelType); + bindingContext.ModelMetadata); var formatters = bindingContext.OperationBindingContext.InputFormatters; var formatter = formatters.FirstOrDefault(f => f.CanRead(formatterContext)); @@ -95,7 +95,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding } catch (Exception ex) { - bindingContext.ModelState.AddModelError(modelBindingKey, ex); + bindingContext.ModelState.AddModelError(modelBindingKey, ex, bindingContext.ModelMetadata); // This model binder is the only handler for the Body binding source and it cannot run twice. Always // tell the model binding system to skip other model binders and never to fall back i.e. indicate a diff --git a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/ByteArrayModelBinder.cs b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/ByteArrayModelBinder.cs index fd5d86adca..eada01b85c 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/ByteArrayModelBinder.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/ByteArrayModelBinder.cs @@ -50,9 +50,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding var model = Convert.FromBase64String(value); return ModelBindingResult.SuccessAsync(bindingContext.ModelName, model); } - catch (Exception ex) + catch (Exception exception) { - bindingContext.ModelState.TryAddModelError(bindingContext.ModelName, ex); + bindingContext.ModelState.TryAddModelError( + bindingContext.ModelName, + exception, + bindingContext.ModelMetadata); } // Matched the type (byte[]) only this binder supports. As in missing data cases, always tell the model diff --git a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/MutableObjectModelBinder.cs b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/MutableObjectModelBinder.cs index e94fa618fb..439c682b4c 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/MutableObjectModelBinder.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/MutableObjectModelBinder.cs @@ -592,7 +592,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding var validationState = modelState.GetFieldValidationState(modelStateKey); if (validationState == ModelValidationState.Unvalidated) { - modelState.AddModelError(modelStateKey, exception); + modelState.AddModelError(modelStateKey, exception, bindingContext.ModelMetadata); } } diff --git a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/SimpleTypeModelBinder.cs b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/SimpleTypeModelBinder.cs index bc36685396..4963002370 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ModelBinding/SimpleTypeModelBinder.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ModelBinding/SimpleTypeModelBinder.cs @@ -60,9 +60,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding return ModelBindingResult.SuccessAsync(bindingContext.ModelName, model); } } - catch (Exception ex) + catch (Exception exception) { - bindingContext.ModelState.TryAddModelError(bindingContext.ModelName, ex); + bindingContext.ModelState.TryAddModelError( + bindingContext.ModelName, + exception, + bindingContext.ModelMetadata); // Were able to find a converter for the type but conversion failed. // Tell the model binding system to skip other model binders. diff --git a/src/Microsoft.AspNet.Mvc.Formatters.Json/JsonInputFormatter.cs b/src/Microsoft.AspNet.Mvc.Formatters.Json/JsonInputFormatter.cs index f94ef448ef..badf691390 100644 --- a/src/Microsoft.AspNet.Mvc.Formatters.Json/JsonInputFormatter.cs +++ b/src/Microsoft.AspNet.Mvc.Formatters.Json/JsonInputFormatter.cs @@ -6,7 +6,7 @@ using System.IO; using System.Text; using System.Threading.Tasks; using Microsoft.AspNet.Mvc.Internal; -using Microsoft.Net.Http.Headers; +using Microsoft.AspNet.Mvc.ModelBinding; using Newtonsoft.Json; namespace Microsoft.AspNet.Mvc.Formatters @@ -101,7 +101,8 @@ namespace Microsoft.AspNet.Mvc.Formatters } } - context.ModelState.TryAddModelError(key, eventArgs.ErrorContext.Error); + var metadata = GetPathMetadata(context.Metadata, eventArgs.ErrorContext.Path); + context.ModelState.TryAddModelError(key, eventArgs.ErrorContext.Error, metadata); // Error must always be marked as handled // Failure to do so can cause the exception to be rethrown at every recursive level and @@ -171,5 +172,51 @@ namespace Microsoft.AspNet.Mvc.Formatters { return JsonSerializer.Create(SerializerSettings); } + + private ModelMetadata GetPathMetadata(ModelMetadata metadata, string path) + { + var index = 0; + while (index >= 0 && index < path.Length) + { + if (path[index] == '[') + { + // At start of "[0]". + if (metadata.ElementMetadata == null) + { + // Odd case but don't throw just because ErrorContext had an odd-looking path. + break; + } + + metadata = metadata.ElementMetadata; + index = path.IndexOf(']', index); + } + else if (path[index] == '.' || path[index] == ']') + { + // Skip '.' in "prefix.property" or "[0].property" or ']' in "[0]". + index++; + } + else + { + // At start of "property", "property." or "property[0]". + var endIndex = path.IndexOfAny(new[] { '.', '[' }, index); + if (endIndex == -1) + { + endIndex = path.Length; + } + + var propertyName = path.Substring(index, endIndex - index); + if (metadata.Properties[propertyName] == null) + { + // Odd case but don't throw just because ErrorContext had an odd-looking path. + break; + } + + metadata = metadata.Properties[propertyName]; + index = endIndex; + } + } + + return metadata; + } } } diff --git a/src/Microsoft.AspNet.Mvc.ViewFeatures/ModelStateDictionaryExtensions.cs b/src/Microsoft.AspNet.Mvc.ViewFeatures/ModelStateDictionaryExtensions.cs index 4e65b3ba9c..58c2f71501 100644 --- a/src/Microsoft.AspNet.Mvc.ViewFeatures/ModelStateDictionaryExtensions.cs +++ b/src/Microsoft.AspNet.Mvc.ViewFeatures/ModelStateDictionaryExtensions.cs @@ -26,6 +26,21 @@ namespace Microsoft.AspNet.Mvc.ModelBinding Expression> expression, string errorMessage) { + if (modelState == null) + { + throw new ArgumentNullException(nameof(modelState)); + } + + if (expression == null) + { + throw new ArgumentNullException(nameof(expression)); + } + + if (errorMessage == null) + { + throw new ArgumentNullException(nameof(errorMessage)); + } + modelState.AddModelError(GetExpressionText(expression), errorMessage); } @@ -40,9 +55,25 @@ namespace Microsoft.AspNet.Mvc.ModelBinding public static void AddModelError( this ModelStateDictionary modelState, Expression> expression, - Exception exception) + Exception exception, + ModelMetadata metadata) { - modelState.AddModelError(GetExpressionText(expression), exception); + if (modelState == null) + { + throw new ArgumentNullException(nameof(modelState)); + } + + if (expression == null) + { + throw new ArgumentNullException(nameof(expression)); + } + + if (metadata == null) + { + throw new ArgumentNullException(nameof(metadata)); + } + + modelState.AddModelError(GetExpressionText(expression), exception, metadata); } /// @@ -59,6 +90,16 @@ namespace Microsoft.AspNet.Mvc.ModelBinding this ModelStateDictionary modelState, Expression> expression) { + if (modelState == null) + { + throw new ArgumentNullException(nameof(modelState)); + } + + if (expression == null) + { + throw new ArgumentNullException(nameof(expression)); + } + return modelState.Remove(GetExpressionText(expression)); } @@ -73,6 +114,16 @@ namespace Microsoft.AspNet.Mvc.ModelBinding this ModelStateDictionary modelState, Expression> expression) { + if (modelState == null) + { + throw new ArgumentNullException(nameof(modelState)); + } + + if (expression == null) + { + throw new ArgumentNullException(nameof(expression)); + } + string modelKey = GetExpressionText(expression); if (string.IsNullOrEmpty(modelKey)) { diff --git a/test/Microsoft.AspNet.Mvc.Abstractions.Test/ModelBinding/ModelStateDictionaryTest.cs b/test/Microsoft.AspNet.Mvc.Abstractions.Test/ModelBinding/ModelStateDictionaryTest.cs index 2bc7e3667e..f99c08ff89 100644 --- a/test/Microsoft.AspNet.Mvc.Abstractions.Test/ModelBinding/ModelStateDictionaryTest.cs +++ b/test/Microsoft.AspNet.Mvc.Abstractions.Test/ModelBinding/ModelStateDictionaryTest.cs @@ -179,10 +179,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding // Arrange var dictionary = new ModelStateDictionary(); dictionary.AddModelError("some key", "some error"); - var ex = new Exception(); + var exception = new Exception(); + var provider = new EmptyModelMetadataProvider(); + var metadata = provider.GetMetadataForProperty(typeof(string), nameof(string.Length)); // Act - dictionary.AddModelError("some key", ex); + dictionary.AddModelError("some key", exception, metadata); // Assert Assert.Equal(2, dictionary.ErrorCount); @@ -191,7 +193,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding Assert.Equal(2, kvp.Value.Errors.Count); Assert.Equal("some error", kvp.Value.Errors[0].ErrorMessage); - Assert.Same(ex, kvp.Value.Errors[1].Exception); + Assert.Same(exception, kvp.Value.Errors[1].Exception); } [Fact] @@ -546,9 +548,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding { MaxAllowedErrors = 5 }; + var provider = new EmptyModelMetadataProvider(); + var metadata = provider.GetMetadataForProperty(typeof(string), nameof(string.Length)); dictionary.AddModelError("key1", "error1"); - dictionary.AddModelError("key2", new Exception()); - dictionary.AddModelError("key3", new Exception()); + dictionary.AddModelError("key2", new Exception(), metadata); + dictionary.AddModelError("key3", new Exception(), metadata); dictionary.AddModelError("key4", "error4"); dictionary.AddModelError("key5", "error5"); @@ -572,6 +576,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding { MaxAllowedErrors = 3 }; + var provider = new EmptyModelMetadataProvider(); + var metadata = provider.GetMetadataForProperty(typeof(string), nameof(string.Length)); // Act and Assert Assert.False(dictionary.HasReachedMaxErrors); @@ -579,7 +585,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding Assert.True(result); Assert.False(dictionary.HasReachedMaxErrors); - result = dictionary.TryAddModelError("key2", new Exception()); + result = dictionary.TryAddModelError("key2", new Exception(), metadata); Assert.True(result); Assert.False(dictionary.HasReachedMaxErrors); // Still room for TooManyModelErrorsException. @@ -614,10 +620,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding { MaxAllowedErrors = 4 }; - dictionary.AddModelError("key1", new Exception()); + var provider = new EmptyModelMetadataProvider(); + var metadata = provider.GetMetadataForProperty(typeof(string), nameof(string.Length)); + dictionary.AddModelError("key1", new Exception(), metadata); dictionary.AddModelError("key2", "error2"); dictionary.AddModelError("key3", "error3"); - dictionary.AddModelError("key3", new Exception()); + dictionary.AddModelError("key3", new Exception(), metadata); // Act and Assert Assert.True(dictionary.HasReachedMaxErrors); @@ -642,15 +650,17 @@ namespace Microsoft.AspNet.Mvc.ModelBinding { MaxAllowedErrors = 3 }; + var provider = new EmptyModelMetadataProvider(); + var metadata = provider.GetMetadataForProperty(typeof(string), nameof(string.Length)); // Act and Assert var result = dictionary.TryAddModelError("key1", "error1"); Assert.True(result); - result = dictionary.TryAddModelError("key2", new Exception()); + result = dictionary.TryAddModelError("key2", new Exception(), metadata); Assert.True(result); - result = dictionary.TryAddModelError("key3", new Exception()); + result = dictionary.TryAddModelError("key3", new Exception(), metadata); Assert.False(result); Assert.Equal(3, dictionary.Count); @@ -668,10 +678,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding { MaxAllowedErrors = 3 }; + var provider = new EmptyModelMetadataProvider(); + var metadata = provider.GetMetadataForProperty(typeof(string), nameof(string.Length)); // Act dictionary.AddModelError("key1", "error1"); - dictionary.TryAddModelError("key3", new Exception()); + dictionary.TryAddModelError("key3", new Exception(), metadata); var copy = new ModelStateDictionary(dictionary); copy.AddModelError("key2", "error2"); @@ -711,11 +723,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding public void ModelStateDictionary_ReturnGenericErrorMessage_WhenModelStateNotSet() { // Arrange - var expected = "The supplied value is invalid for key."; + var expected = "The supplied value is invalid for Length."; var dictionary = new ModelStateDictionary(); + var provider = new EmptyModelMetadataProvider(); + var metadata = provider.GetMetadataForProperty(typeof(string), nameof(string.Length)); // Act - dictionary.TryAddModelError("key", new FormatException()); + dictionary.TryAddModelError("key", new FormatException(), metadata); // Assert var error = Assert.Single(dictionary["key"].Errors); @@ -726,12 +740,14 @@ namespace Microsoft.AspNet.Mvc.ModelBinding public void ModelStateDictionary_ReturnSpecificErrorMessage_WhenModelStateSet() { // Arrange - var expected = "The value 'some value' is not valid for key."; + var expected = "The value 'some value' is not valid for Length."; var dictionary = new ModelStateDictionary(); dictionary.SetModelValue("key", new string[] { "some value" }, "some value"); + var provider = new EmptyModelMetadataProvider(); + var metadata = provider.GetMetadataForProperty(typeof(string), nameof(string.Length)); // Act - dictionary.TryAddModelError("key", new FormatException()); + dictionary.TryAddModelError("key", new FormatException(), metadata); // Assert var error = Assert.Single(dictionary["key"].Errors); @@ -744,9 +760,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding // Arrange var dictionary = new ModelStateDictionary(); dictionary.SetModelValue("key", new string[] { "some value" }, "some value"); + var provider = new EmptyModelMetadataProvider(); + var metadata = provider.GetMetadataForProperty(typeof(string), nameof(string.Length)); // Act - dictionary.TryAddModelError("key", new InvalidOperationException()); + dictionary.TryAddModelError("key", new InvalidOperationException(), metadata); // Assert var error = Assert.Single(dictionary["key"].Errors); diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/Formatters/InputFormatterTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/Formatters/InputFormatterTest.cs index dd7fca8b8e..edce8d3bfd 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/Formatters/InputFormatterTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/Formatters/InputFormatterTest.cs @@ -36,11 +36,14 @@ namespace Microsoft.AspNet.Mvc.Formatters var formatter = new CatchAllFormatter(); var httpContext = new DefaultHttpContext(); httpContext.Request.ContentType = requestContentType; + + var provider = new EmptyModelMetadataProvider(); + var metadata = provider.GetMetadataForType(typeof(void)); var context = new InputFormatterContext( httpContext, modelName: string.Empty, modelState: new ModelStateDictionary(), - modelType: typeof(void)); + metadata: metadata); // Act var result = formatter.CanRead(context); @@ -66,11 +69,14 @@ namespace Microsoft.AspNet.Mvc.Formatters var formatter = new MultipartFormatter(); var httpContext = new DefaultHttpContext(); httpContext.Request.ContentType = requestContentType; + + var provider = new EmptyModelMetadataProvider(); + var metadata = provider.GetMetadataForType(typeof(void)); var context = new InputFormatterContext( httpContext, modelName: string.Empty, modelState: new ModelStateDictionary(), - modelType: typeof(void)); + metadata: metadata); // Act var result = formatter.CanRead(context); @@ -93,11 +99,14 @@ namespace Microsoft.AspNet.Mvc.Formatters var formatter = new MultipartFormatter(); var httpContext = new DefaultHttpContext(); httpContext.Request.ContentType = requestContentType; + + var provider = new EmptyModelMetadataProvider(); + var metadata = provider.GetMetadataForType(typeof(void)); var context = new InputFormatterContext( httpContext, modelName: string.Empty, modelState: new ModelStateDictionary(), - modelType: typeof(void)); + metadata: metadata); // Act var result = formatter.CanRead(context); @@ -123,11 +132,14 @@ namespace Microsoft.AspNet.Mvc.Formatters var formatter = new MultipartMixedFormatter(); var httpContext = new DefaultHttpContext(); httpContext.Request.ContentType = requestContentType; + + var provider = new EmptyModelMetadataProvider(); + var metadata = provider.GetMetadataForType(typeof(void)); var context = new InputFormatterContext( httpContext, modelName: string.Empty, modelState: new ModelStateDictionary(), - modelType: typeof(void)); + metadata: metadata); // Act var result = formatter.CanRead(context); @@ -150,11 +162,14 @@ namespace Microsoft.AspNet.Mvc.Formatters var formatter = new MultipartMixedFormatter(); var httpContext = new DefaultHttpContext(); httpContext.Request.ContentType = requestContentType; + + var provider = new EmptyModelMetadataProvider(); + var metadata = provider.GetMetadataForType(typeof(void)); var context = new InputFormatterContext( httpContext, modelName: string.Empty, modelState: new ModelStateDictionary(), - modelType: typeof(void)); + metadata: metadata); // Act var result = formatter.CanRead(context); @@ -183,11 +198,14 @@ namespace Microsoft.AspNet.Mvc.Formatters var formatter = new MathMLFormatter(); var httpContext = new DefaultHttpContext(); httpContext.Request.ContentType = requestContentType; + + var provider = new EmptyModelMetadataProvider(); + var metadata = provider.GetMetadataForType(typeof(void)); var context = new InputFormatterContext( httpContext, modelName: string.Empty, modelState: new ModelStateDictionary(), - modelType: typeof(void)); + metadata: metadata); // Act var result = formatter.CanRead(context); @@ -209,11 +227,14 @@ namespace Microsoft.AspNet.Mvc.Formatters var formatter = new MathMLFormatter(); var httpContext = new DefaultHttpContext(); httpContext.Request.ContentType = requestContentType; + + var provider = new EmptyModelMetadataProvider(); + var metadata = provider.GetMetadataForType(typeof(void)); var context = new InputFormatterContext( httpContext, modelName: string.Empty, modelState: new ModelStateDictionary(), - modelType: typeof(void)); + metadata: metadata); // Act var result = formatter.CanRead(context); @@ -240,11 +261,14 @@ namespace Microsoft.AspNet.Mvc.Formatters var formatter = new XmlFormatter(); var httpContext = new DefaultHttpContext(); httpContext.Request.ContentType = requestContentType; + + var provider = new EmptyModelMetadataProvider(); + var metadata = provider.GetMetadataForType(typeof(void)); var context = new InputFormatterContext( httpContext, modelName: string.Empty, modelState: new ModelStateDictionary(), - modelType: typeof(void)); + metadata: metadata); // Act var result = formatter.CanRead(context); @@ -268,11 +292,14 @@ namespace Microsoft.AspNet.Mvc.Formatters var formatter = new XmlFormatter(); var httpContext = new DefaultHttpContext(); httpContext.Request.ContentType = requestContentType; + + var provider = new EmptyModelMetadataProvider(); + var metadata = provider.GetMetadataForType(typeof(void)); var context = new InputFormatterContext( httpContext, modelName: string.Empty, modelState: new ModelStateDictionary(), - modelType: typeof(void)); + metadata: metadata); // Act var result = formatter.CanRead(context); diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/ByteArrayModelBinderTests.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/ByteArrayModelBinderTests.cs index 767a7e473c..b5f8d2bb28 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/ByteArrayModelBinderTests.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/ByteArrayModelBinderTests.cs @@ -62,7 +62,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test public async Task BindModelAddsModelErrorsOnInvalidCharacters() { // Arrange - var expected = "The value '\"Fys1\"' is not valid for foo."; + var expected = "The value '\"Fys1\"' is not valid for Byte[]."; var valueProvider = new SimpleValueProvider() { diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/SimpleTypeModelBinderTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/SimpleTypeModelBinderTest.cs index 36b9c04efd..fcd825d704 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/SimpleTypeModelBinderTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ModelBinding/SimpleTypeModelBinderTest.cs @@ -113,7 +113,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test public async Task BindModel_Error_FormatExceptionsTurnedIntoStringsInModelState() { // Arrange - var message = "The value 'not an integer' is not valid for theModelName."; + var message = "The value 'not an integer' is not valid for Int32."; var bindingContext = GetBindingContext(typeof(int)); bindingContext.ValueProvider = new SimpleValueProvider { diff --git a/test/Microsoft.AspNet.Mvc.Formatters.Json.Test/JsonInputFormatterTest.cs b/test/Microsoft.AspNet.Mvc.Formatters.Json.Test/JsonInputFormatterTest.cs index 5b502b2202..e11be51c8c 100644 --- a/test/Microsoft.AspNet.Mvc.Formatters.Json.Test/JsonInputFormatterTest.cs +++ b/test/Microsoft.AspNet.Mvc.Formatters.Json.Test/JsonInputFormatterTest.cs @@ -38,11 +38,13 @@ namespace Microsoft.AspNet.Mvc.Formatters var contentBytes = Encoding.UTF8.GetBytes("content"); var httpContext = GetHttpContext(contentBytes, contentType: requestContentType); + var provider = new EmptyModelMetadataProvider(); + var metadata = provider.GetMetadataForType(typeof(string)); var formatterContext = new InputFormatterContext( httpContext, modelName: string.Empty, modelState: new ModelStateDictionary(), - modelType: typeof(string)); + metadata: metadata); // Act var result = formatter.CanRead(formatterContext); @@ -84,11 +86,13 @@ namespace Microsoft.AspNet.Mvc.Formatters var contentBytes = Encoding.UTF8.GetBytes(content); var httpContext = GetHttpContext(contentBytes); + var provider = new EmptyModelMetadataProvider(); + var metadata = provider.GetMetadataForType(type); var context = new InputFormatterContext( httpContext, modelName: string.Empty, modelState: new ModelStateDictionary(), - modelType: type); + metadata: metadata); // Act var result = await formatter.ReadAsync(context); @@ -107,11 +111,13 @@ namespace Microsoft.AspNet.Mvc.Formatters var contentBytes = Encoding.UTF8.GetBytes(content); var httpContext = GetHttpContext(contentBytes); + var provider = new EmptyModelMetadataProvider(); + var metadata = provider.GetMetadataForType(typeof(User)); var context = new InputFormatterContext( httpContext, modelName: string.Empty, modelState: new ModelStateDictionary(), - modelType: typeof(User)); + metadata: metadata); // Act var result = await formatter.ReadAsync(context); @@ -133,11 +139,13 @@ namespace Microsoft.AspNet.Mvc.Formatters var modelState = new ModelStateDictionary(); var httpContext = GetHttpContext(contentBytes); + var provider = new EmptyModelMetadataProvider(); + var metadata = provider.GetMetadataForType(typeof(User)); var context = new InputFormatterContext( httpContext, modelName: string.Empty, modelState: modelState, - modelType: typeof(User)); + metadata: metadata); // Act var result = await formatter.ReadAsync(context); @@ -149,6 +157,61 @@ namespace Microsoft.AspNet.Mvc.Formatters modelState["Age"].Errors[0].Exception.Message); } + [Fact] + public async Task ReadAsync_InvalidArray_AddsOverflowErrorsToModelState() + { + // Arrange + var content = "[0, 23, 300]"; + var formatter = new JsonInputFormatter(); + var contentBytes = Encoding.UTF8.GetBytes(content); + + var modelState = new ModelStateDictionary(); + var httpContext = GetHttpContext(contentBytes); + var provider = new EmptyModelMetadataProvider(); + var metadata = provider.GetMetadataForType(typeof(byte[])); + var context = new InputFormatterContext( + httpContext, + modelName: string.Empty, + modelState: modelState, + metadata: metadata); + + // Act + var result = await formatter.ReadAsync(context); + + // Assert + Assert.True(result.HasError); + Assert.Equal("The supplied value is invalid for Byte.", modelState["[2]"].Errors[0].ErrorMessage); + Assert.Null(modelState["[2]"].Errors[0].Exception); + } + + [Fact] + public async Task ReadAsync_InvalidComplexArray_AddsOverflowErrorsToModelState() + { + // Arrange + var content = "[{name: 'Name One', Age: 30}, {name: 'Name Two', Small: 300}]"; + var formatter = new JsonInputFormatter(); + var contentBytes = Encoding.UTF8.GetBytes(content); + + var modelState = new ModelStateDictionary(); + var httpContext = GetHttpContext(contentBytes); + var provider = new EmptyModelMetadataProvider(); + var metadata = provider.GetMetadataForType(typeof(User[])); + var context = new InputFormatterContext( + httpContext, + modelName: "names", + modelState: modelState, + metadata: metadata); + + // Act + var result = await formatter.ReadAsync(context); + + // Assert + Assert.True(result.HasError); + Assert.Equal( + "Error converting value 300 to type 'System.Byte'. Path '[1].Small', line 1, position 59.", + modelState["names[1].Small"].Errors[0].Exception.Message); + } + [Fact] public async Task ReadAsync_UsesTryAddModelValidationErrorsToModelState() { @@ -159,11 +222,13 @@ namespace Microsoft.AspNet.Mvc.Formatters var modelState = new ModelStateDictionary(); var httpContext = GetHttpContext(contentBytes); + var provider = new EmptyModelMetadataProvider(); + var metadata = provider.GetMetadataForType(typeof(User)); var context = new InputFormatterContext( httpContext, modelName: string.Empty, modelState: modelState, - modelType: typeof(User)); + metadata: metadata); modelState.MaxAllowedErrors = 3; modelState.AddModelError("key1", "error1"); @@ -215,11 +280,13 @@ namespace Microsoft.AspNet.Mvc.Formatters var modelState = new ModelStateDictionary(); var httpContext = GetHttpContext(contentBytes, "application/json;charset=utf-8"); + var provider = new EmptyModelMetadataProvider(); + var metadata = provider.GetMetadataForType(typeof(UserLogin)); var inputFormatterContext = new InputFormatterContext( httpContext, modelName: string.Empty, modelState: modelState, - modelType: typeof(UserLogin)); + metadata: metadata); // Act var result = await jsonFormatter.ReadAsync(inputFormatterContext); @@ -248,11 +315,13 @@ namespace Microsoft.AspNet.Mvc.Formatters var modelState = new ModelStateDictionary(); var httpContext = GetHttpContext(contentBytes, "application/json;charset=utf-8"); + var provider = new EmptyModelMetadataProvider(); + var metadata = provider.GetMetadataForType(typeof(UserLogin)); var inputFormatterContext = new InputFormatterContext( httpContext, modelName: string.Empty, modelState: modelState, - modelType: typeof(UserLogin)); + metadata: metadata); // Act var result = await jsonFormatter.ReadAsync(inputFormatterContext); @@ -315,6 +384,8 @@ namespace Microsoft.AspNet.Mvc.Formatters public string Name { get; set; } public decimal Age { get; set; } + + public byte Small { get; set; } } private sealed class UserLogin diff --git a/test/Microsoft.AspNet.Mvc.Formatters.Json.Test/JsonPatchInputFormatterTest.cs b/test/Microsoft.AspNet.Mvc.Formatters.Json.Test/JsonPatchInputFormatterTest.cs index 8b40c2059e..86bb77883b 100644 --- a/test/Microsoft.AspNet.Mvc.Formatters.Json.Test/JsonPatchInputFormatterTest.cs +++ b/test/Microsoft.AspNet.Mvc.Formatters.Json.Test/JsonPatchInputFormatterTest.cs @@ -25,11 +25,13 @@ namespace Microsoft.AspNet.Mvc.Formatters var modelState = new ModelStateDictionary(); var httpContext = GetHttpContext(contentBytes); + var provider = new EmptyModelMetadataProvider(); + var metadata = provider.GetMetadataForType(typeof(JsonPatchDocument)); var context = new InputFormatterContext( httpContext, modelName: string.Empty, modelState: modelState, - modelType: typeof(JsonPatchDocument)); + metadata: metadata); // Act var result = await formatter.ReadAsync(context); @@ -53,11 +55,13 @@ namespace Microsoft.AspNet.Mvc.Formatters var modelState = new ModelStateDictionary(); var httpContext = GetHttpContext(contentBytes); + var provider = new EmptyModelMetadataProvider(); + var metadata = provider.GetMetadataForType(typeof(JsonPatchDocument)); var context = new InputFormatterContext( httpContext, modelName: string.Empty, modelState: modelState, - modelType: typeof(JsonPatchDocument)); + metadata: metadata); // Act var result = await formatter.ReadAsync(context); @@ -86,11 +90,13 @@ namespace Microsoft.AspNet.Mvc.Formatters var modelState = new ModelStateDictionary(); var httpContext = GetHttpContext(contentBytes, contentType: requestContentType); + var provider = new EmptyModelMetadataProvider(); + var metadata = provider.GetMetadataForType(typeof(JsonPatchDocument)); var formatterContext = new InputFormatterContext( httpContext, modelName: string.Empty, modelState: modelState, - modelType: typeof(JsonPatchDocument)); + metadata: metadata); // Act var result = formatter.CanRead(formatterContext); @@ -111,11 +117,13 @@ namespace Microsoft.AspNet.Mvc.Formatters var modelState = new ModelStateDictionary(); var httpContext = GetHttpContext(contentBytes, contentType: "application/json-patch+json"); + var provider = new EmptyModelMetadataProvider(); + var metadata = provider.GetMetadataForType(modelType); var formatterContext = new InputFormatterContext( httpContext, modelName: string.Empty, modelState: modelState, - modelType: modelType); + metadata: metadata); // Act var result = formatter.CanRead(formatterContext); @@ -137,11 +145,13 @@ namespace Microsoft.AspNet.Mvc.Formatters var modelState = new ModelStateDictionary(); var httpContext = GetHttpContext(contentBytes, contentType: "application/json-patch+json"); + var provider = new EmptyModelMetadataProvider(); + var metadata = provider.GetMetadataForType(typeof(Customer)); var context = new InputFormatterContext( httpContext, modelName: string.Empty, modelState: modelState, - modelType: typeof(Customer)); + metadata: metadata); // Act var result = await formatter.ReadAsync(context); diff --git a/test/Microsoft.AspNet.Mvc.Formatters.Xml.Test/XmlDataContractSerializerInputFormatterTest.cs b/test/Microsoft.AspNet.Mvc.Formatters.Xml.Test/XmlDataContractSerializerInputFormatterTest.cs index b16c0994eb..f4b3e17db3 100644 --- a/test/Microsoft.AspNet.Mvc.Formatters.Xml.Test/XmlDataContractSerializerInputFormatterTest.cs +++ b/test/Microsoft.AspNet.Mvc.Formatters.Xml.Test/XmlDataContractSerializerInputFormatterTest.cs @@ -73,11 +73,13 @@ namespace Microsoft.AspNet.Mvc.Formatters.Xml var modelState = new ModelStateDictionary(); var httpContext = GetHttpContext(contentBytes, contentType: requestContentType); + var provider = new EmptyModelMetadataProvider(); + var metadata = provider.GetMetadataForType(typeof(string)); var formatterContext = new InputFormatterContext( httpContext, modelName: string.Empty, modelState: modelState, - modelType: typeof(string)); + metadata: metadata); // Act var result = formatter.CanRead(formatterContext); @@ -337,11 +339,13 @@ namespace Microsoft.AspNet.Mvc.Formatters.Xml var modelState = new ModelStateDictionary(); var httpContext = GetHttpContext(inputBytes, contentType: "application/xml; charset=utf-16"); + var provider = new EmptyModelMetadataProvider(); + var metadata = provider.GetMetadataForType(typeof(TestLevelOne)); var context = new InputFormatterContext( httpContext, modelName: string.Empty, modelState: modelState, - modelType: typeof(TestLevelOne)); + metadata: metadata); // Act var ex = await Assert.ThrowsAsync(expectedException, () => formatter.ReadAsync(context)); @@ -401,11 +405,13 @@ namespace Microsoft.AspNet.Mvc.Formatters.Xml var modelState = new ModelStateDictionary(); var httpContext = GetHttpContext(contentBytes, contentType: "application/xml; charset=utf-16"); + var provider = new EmptyModelMetadataProvider(); + var metadata = provider.GetMetadataForType(typeof(TestLevelOne)); var context = new InputFormatterContext( httpContext, modelName: string.Empty, modelState: modelState, - modelType: typeof(TestLevelOne)); + metadata: metadata); // Act var result = await formatter.ReadAsync(context); @@ -544,11 +550,13 @@ namespace Microsoft.AspNet.Mvc.Formatters.Xml private InputFormatterContext GetInputFormatterContext(byte[] contentBytes, Type modelType) { var httpContext = GetHttpContext(contentBytes); + var provider = new EmptyModelMetadataProvider(); + var metadata = provider.GetMetadataForType(modelType); return new InputFormatterContext( httpContext, modelName: string.Empty, modelState: new ModelStateDictionary(), - modelType: modelType); + metadata: metadata); } private static HttpContext GetHttpContext( diff --git a/test/Microsoft.AspNet.Mvc.Formatters.Xml.Test/XmlSerializerInputFormatterTest.cs b/test/Microsoft.AspNet.Mvc.Formatters.Xml.Test/XmlSerializerInputFormatterTest.cs index 5205fc9c73..4a11d84d85 100644 --- a/test/Microsoft.AspNet.Mvc.Formatters.Xml.Test/XmlSerializerInputFormatterTest.cs +++ b/test/Microsoft.AspNet.Mvc.Formatters.Xml.Test/XmlSerializerInputFormatterTest.cs @@ -59,11 +59,13 @@ namespace Microsoft.AspNet.Mvc.Formatters.Xml var modelState = new ModelStateDictionary(); var httpContext = GetHttpContext(contentBytes, contentType: requestContentType); + var provider = new EmptyModelMetadataProvider(); + var metadata = provider.GetMetadataForType(typeof(string)); var formatterContext = new InputFormatterContext( httpContext, modelName: string.Empty, modelState: modelState, - modelType: typeof(string)); + metadata: metadata); // Act var result = formatter.CanRead(formatterContext); @@ -342,11 +344,13 @@ namespace Microsoft.AspNet.Mvc.Formatters.Xml var modelState = new ModelStateDictionary(); var httpContext = GetHttpContext(inputBytes, contentType: "application/xml; charset=utf-16"); + var provider = new EmptyModelMetadataProvider(); + var metadata = provider.GetMetadataForType(typeof(TestLevelOne)); var context = new InputFormatterContext( httpContext, modelName: string.Empty, modelState: modelState, - modelType: typeof(TestLevelOne)); + metadata: metadata); // Act and Assert var ex = await Assert.ThrowsAsync(expectedException, () => formatter.ReadAsync(context)); @@ -403,11 +407,13 @@ namespace Microsoft.AspNet.Mvc.Formatters.Xml var modelState = new ModelStateDictionary(); var httpContext = GetHttpContext(contentBytes, contentType: "application/xml; charset=utf-16"); + var provider = new EmptyModelMetadataProvider(); + var metadata = provider.GetMetadataForType(typeof(TestLevelOne)); var context = new InputFormatterContext( httpContext, modelName: string.Empty, modelState: modelState, - modelType: typeof(TestLevelOne)); + metadata: metadata); // Act var result = await formatter.ReadAsync(context); @@ -425,11 +431,13 @@ namespace Microsoft.AspNet.Mvc.Formatters.Xml private InputFormatterContext GetInputFormatterContext(byte[] contentBytes, Type modelType) { var httpContext = GetHttpContext(contentBytes); + var provider = new EmptyModelMetadataProvider(); + var metadata = provider.GetMetadataForType(modelType); return new InputFormatterContext( httpContext, modelName: string.Empty, modelState: new ModelStateDictionary(), - modelType: modelType); + metadata: metadata); } private static HttpContext GetHttpContext( diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/ModelBindingTest.cs b/test/Microsoft.AspNet.Mvc.FunctionalTests/ModelBindingTest.cs index c2013d3f7d..726275d7b9 100644 --- a/test/Microsoft.AspNet.Mvc.FunctionalTests/ModelBindingTest.cs +++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/ModelBindingTest.cs @@ -1443,7 +1443,7 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); var result = await response.Content.ReadAsStringAsync(); - Assert.Equal("The value 'random string' is not valid for birthdate.", result); + Assert.Equal("The value 'random string' is not valid for DateTime.", result); } [Fact] diff --git a/test/Microsoft.AspNet.Mvc.IntegrationTests/SimpleTypeModelBinderIntegrationTest.cs b/test/Microsoft.AspNet.Mvc.IntegrationTests/SimpleTypeModelBinderIntegrationTest.cs index f647c18fbb..a61b67d16f 100644 --- a/test/Microsoft.AspNet.Mvc.IntegrationTests/SimpleTypeModelBinderIntegrationTest.cs +++ b/test/Microsoft.AspNet.Mvc.IntegrationTests/SimpleTypeModelBinderIntegrationTest.cs @@ -244,7 +244,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests var error = Assert.Single(entry.Errors); Assert.Null(error.Exception); - Assert.Equal("The value 'abcd' is not valid for Parameter1.", error.ErrorMessage); + Assert.Equal("The value 'abcd' is not valid for Int32.", error.ErrorMessage); } [Theory] diff --git a/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/ModelStateDictionaryExtensionsTest.cs b/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/ModelStateDictionaryExtensionsTest.cs index 923286330b..66fcab72c6 100644 --- a/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/ModelStateDictionaryExtensionsTest.cs +++ b/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/ModelStateDictionaryExtensionsTest.cs @@ -81,11 +81,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding public void AddModelError_ForSingleExpression_AddsExpectedException() { // Arrange - var exception = new Exception(); var dictionary = new ModelStateDictionary(); + var exception = new Exception(); + var provider = new TestModelMetadataProvider(); + var metadata = provider.GetMetadataForProperty(typeof(TestModel), nameof(TestModel.Text)); // Act - dictionary.AddModelError(model => model.Text, exception); + dictionary.AddModelError(model => model.Text, exception, metadata); // Assert var modelState = Assert.Single(dictionary); @@ -99,11 +101,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding public void AddModelError_ForRelationExpression_AddsExpectedException() { // Arrange - var exception = new Exception(); var dictionary = new ModelStateDictionary(); + var exception = new Exception(); + var provider = new TestModelMetadataProvider(); + var metadata = provider.GetMetadataForProperty(typeof(ChildModel), nameof(ChildModel.Text)); // Act - dictionary.AddModelError(model => model.Child.Text, exception); + dictionary.AddModelError(model => model.Child.Text, exception, metadata); // Assert var modelState = Assert.Single(dictionary); @@ -117,11 +121,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding public void AddModelError_ForImplicitlyCastedToObjectExpression_AddsExpectedException() { // Arrange - var exception = new Exception(); var dictionary = new ModelStateDictionary(); + var exception = new Exception(); + var provider = new TestModelMetadataProvider(); + var metadata = provider.GetMetadataForProperty(typeof(ChildModel), nameof(ChildModel.Value)); // Act - dictionary.AddModelError(model => model.Child.Value, exception); + dictionary.AddModelError(model => model.Child.Value, exception, metadata); // Assert var modelState = Assert.Single(dictionary); @@ -135,12 +141,14 @@ namespace Microsoft.AspNet.Mvc.ModelBinding public void AddModelError_ForNotModelsExpression_AddsExpectedException() { // Arrange + var dictionary = new ModelStateDictionary(); var variable = "Test"; var exception = new Exception(); - var dictionary = new ModelStateDictionary(); + var provider = new TestModelMetadataProvider(); + var metadata = provider.GetMetadataForProperty(typeof(string), nameof(string.Length)); // Act - dictionary.AddModelError(model => variable, exception); + dictionary.AddModelError(model => variable, exception, metadata); // Assert var modelState = Assert.Single(dictionary); diff --git a/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/Rendering/HtmlHelperValidationSummaryTest.cs b/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/Rendering/HtmlHelperValidationSummaryTest.cs index 5e79d59e70..325bf1914a 100644 --- a/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/Rendering/HtmlHelperValidationSummaryTest.cs +++ b/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/Rendering/HtmlHelperValidationSummaryTest.cs @@ -343,23 +343,29 @@ namespace Microsoft.AspNet.Mvc.Rendering modelState.AddModelError("Property3.OrderedProperty3", "This is an error for Property3.OrderedProperty3."); modelState.AddModelError("Property3.OrderedProperty2", "This is an error for Property3.OrderedProperty2."); + var provider = new EmptyModelMetadataProvider(); + var metadata = provider.GetMetadataForProperty(typeof(ValidationModel), nameof(ValidationModel.Property3)); modelState.AddModelError("Property3", "This is an error for Property3."); - modelState.AddModelError("Property3", new InvalidCastException("Exception will be ignored.")); + modelState.AddModelError("Property3", new InvalidCastException("Exception will be ignored."), metadata); + metadata = provider.GetMetadataForProperty(typeof(ValidationModel), nameof(ValidationModel.Property2)); modelState.AddModelError("Property2", "This is an error for Property2."); modelState.AddModelError("Property2", "This is another error for Property2."); - modelState.AddModelError("Property2", new OverflowException("Produces invalid value message")); + modelState.AddModelError("Property2", new OverflowException("Produces invalid value message"), metadata); + metadata = provider.GetMetadataForType(typeof(ValidationModel)); modelState.AddModelError(string.Empty, "This is an error for the model root."); modelState.AddModelError(string.Empty, "This is another error for the model root."); - modelState.AddModelError(string.Empty, new InvalidOperationException("Another ignored Exception.")); + modelState.AddModelError(string.Empty, new InvalidOperationException("Another ignored Exception."), metadata); } // Adds one or more errors for all properties in OrderedModel. But adds errors out of order. private void AddOrderedErrors(ModelStateDictionary modelState) { + var provider = new EmptyModelMetadataProvider(); + var metadata = provider.GetMetadataForProperty(typeof(OrderedModel), nameof(OrderedModel.Property3)); modelState.AddModelError("Property3", "This is an error for Property3."); - modelState.AddModelError("Property3", new InvalidCastException("An ignored Exception.")); + modelState.AddModelError("Property3", new InvalidCastException("An ignored Exception."), metadata); modelState.AddModelError("Property2", "This is an error for Property2."); modelState.AddModelError("Property2", "This is another error for Property2."); diff --git a/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/HttpErrorTest.cs b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/HttpErrorTest.cs index 5d698eddcb..de1acc77a4 100644 --- a/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/HttpErrorTest.cs +++ b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/HttpErrorTest.cs @@ -66,13 +66,19 @@ namespace System.Web.Http.Dispatcher [Fact] public void ModelStateConstructorWithDetail_AddsCorrectDictionaryItems() { + // Arrange ModelStateDictionary modelState = new ModelStateDictionary(); + var provider = new EmptyModelMetadataProvider(); + var metadata = provider.GetMetadataForProperty(typeof(string), nameof(string.Length)); modelState.AddModelError("[0].Name", "error1"); modelState.AddModelError("[0].Name", "error2"); modelState.AddModelError("[0].Address", "error"); - modelState.AddModelError("[2].Name", new Exception("OH NO")); + modelState.AddModelError("[2].Name", new Exception("OH NO"), metadata); + // Act HttpError error = new HttpError(modelState, true); + + // Assert HttpError modelStateError = error["ModelState"] as HttpError; Assert.Contains(new KeyValuePair("Message", "The request is invalid."), error); @@ -98,13 +104,19 @@ namespace System.Web.Http.Dispatcher [Fact] public void ModelStateConstructorWithoutDetail_AddsCorrectDictionaryItems() { + // Arrange ModelStateDictionary modelState = new ModelStateDictionary(); + var provider = new EmptyModelMetadataProvider(); + var metadata = provider.GetMetadataForProperty(typeof(string), nameof(string.Length)); modelState.AddModelError("[0].Name", "error1"); modelState.AddModelError("[0].Name", "error2"); modelState.AddModelError("[0].Address", "error"); - modelState.AddModelError("[2].Name", new Exception("OH NO")); + modelState.AddModelError("[2].Name", new Exception("OH NO"), metadata); + // Act HttpError error = new HttpError(modelState, false); + + // Assert HttpError modelStateError = error["ModelState"] as HttpError; Assert.Contains(new KeyValuePair("Message", "The request is invalid."), error); diff --git a/test/WebSites/XmlFormattersWebSite/Controllers/SerializableErrorController.cs b/test/WebSites/XmlFormattersWebSite/Controllers/SerializableErrorController.cs index d569271517..5a90992907 100644 --- a/test/WebSites/XmlFormattersWebSite/Controllers/SerializableErrorController.cs +++ b/test/WebSites/XmlFormattersWebSite/Controllers/SerializableErrorController.cs @@ -24,7 +24,7 @@ namespace XmlFormattersWebSite.Controllers } ModelState.AddModelError("key1", "key1-error"); - ModelState.AddModelError("key2", exception); + ModelState.AddModelError("key2", exception, ViewData.ModelMetadata); return new ObjectResult(new SerializableError(ModelState)); }