From c923ad252dddfbb0671c0f6c2d4be1107a98cc6d Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Mon, 13 Dec 2021 13:18:27 -0800 Subject: [PATCH 01/16] Core functionality --- .../src/ModelBinding/ModelMetadata.cs | 5 ++ .../Infrastructure/MvcCoreMvcOptionsSetup.cs | 7 ++ .../Metadata/DefaultModelMetadata.cs | 3 + .../Metadata/JsonMetadataProvider.cs | 75 +++++++++++++++++++ .../Metadata/ValidationMetadata.cs | 5 ++ .../DefaultComplexObjectValidationStrategy.cs | 2 +- src/Mvc/Mvc.Core/src/MvcOptions.cs | 6 ++ src/Mvc/Mvc.Core/src/PublicAPI.Unshipped.txt | 4 + .../NewtonsoftJsonMvcOptionsSetup.cs | 8 ++ .../src/NewtonsoftJsonMetadataProvider.cs | 58 ++++++++++++++ 10 files changed, 172 insertions(+), 1 deletion(-) create mode 100644 src/Mvc/Mvc.Core/src/ModelBinding/Metadata/JsonMetadataProvider.cs create mode 100644 src/Mvc/Mvc.NewtonsoftJson/src/NewtonsoftJsonMetadataProvider.cs diff --git a/src/Mvc/Mvc.Abstractions/src/ModelBinding/ModelMetadata.cs b/src/Mvc/Mvc.Abstractions/src/ModelBinding/ModelMetadata.cs index ccb3869ee305..64ff920ba92f 100644 --- a/src/Mvc/Mvc.Abstractions/src/ModelBinding/ModelMetadata.cs +++ b/src/Mvc/Mvc.Abstractions/src/ModelBinding/ModelMetadata.cs @@ -498,6 +498,11 @@ internal IReadOnlyDictionary BoundConstructorPrope /// internal virtual bool PropertyHasValidators => false; + /// + /// Gets the name of a model, if specified explicitly, to be used on + /// + internal abstract string? ValidationModelName { get; } + /// /// Throws if the ModelMetadata is for a record type with validation on properties. /// diff --git a/src/Mvc/Mvc.Core/src/Infrastructure/MvcCoreMvcOptionsSetup.cs b/src/Mvc/Mvc.Core/src/Infrastructure/MvcCoreMvcOptionsSetup.cs index 1f317510be39..a8f3e6561f52 100644 --- a/src/Mvc/Mvc.Core/src/Infrastructure/MvcCoreMvcOptionsSetup.cs +++ b/src/Mvc/Mvc.Core/src/Infrastructure/MvcCoreMvcOptionsSetup.cs @@ -116,6 +116,11 @@ public void PostConfigure(string name, MvcOptions options) // This should ensure it appears later than all of the details provider registered by MVC and user configured details provider registered // as part of ConfigureOptions. options.ModelMetadataDetailsProviders.Add(new HasValidatorsValidationMetadataProvider(options.ModelValidatorProviders)); + + if (options.RespectModelNameInValidationErrors) + { + options.ModelMetadataDetailsProviders.Add(new JsonMetadataProvider(_jsonOptions.Value?.JsonSerializerOptions?.PropertyNamingPolicy)); + } } internal static void ConfigureAdditionalModelMetadataDetailsProviders(IList modelMetadataDetailsProviders) @@ -146,5 +151,7 @@ internal static void ConfigureAdditionalModelMetadataDetailsProviders(IList ValidationMetadata.PropertyHasValidators; + /// + internal override string? ValidationModelName => ValidationMetadata.ValidationModelName; + internal static bool CalculateHasValidators(HashSet visited, ModelMetadata metadata) { RuntimeHelpers.EnsureSufficientExecutionStack(); diff --git a/src/Mvc/Mvc.Core/src/ModelBinding/Metadata/JsonMetadataProvider.cs b/src/Mvc/Mvc.Core/src/ModelBinding/Metadata/JsonMetadataProvider.cs new file mode 100644 index 000000000000..c93f040906bd --- /dev/null +++ b/src/Mvc/Mvc.Core/src/ModelBinding/Metadata/JsonMetadataProvider.cs @@ -0,0 +1,75 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#nullable enable + +using System.Linq; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata; + +/// +/// An implementation of and for +/// the System.Text.Json.Serialization attribute classes. +/// +internal class JsonMetadataProvider : IDisplayMetadataProvider, IValidationMetadataProvider +{ + private readonly JsonNamingPolicy? _jsonNamingPolicy; + + /// + /// Creates a new . + /// + public JsonMetadataProvider() + : this(JsonNamingPolicy.CamelCase) + { + } + + /// + /// Creates a new with an optional + /// + /// The to be used to convert the property name + public JsonMetadataProvider(JsonNamingPolicy? jsonNamingPolicy) + => _jsonNamingPolicy = jsonNamingPolicy; + + /// + public void CreateDisplayMetadata(DisplayMetadataProviderContext context) + { + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + + var propertyName = ReadPropertyNameFrom(context.Attributes); + + if (!string.IsNullOrEmpty(propertyName)) + { + context.DisplayMetadata.DisplayName = () => propertyName; + } + } + + /// + public void CreateValidationMetadata(ValidationMetadataProviderContext context) + { + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + + var propertyName = ReadPropertyNameFrom(context.Attributes); + + if (string.IsNullOrEmpty(propertyName)) + { + propertyName = _jsonNamingPolicy?.ConvertName(context.Key.Name!) ?? context.Key.Name; + } + + context.ValidationMetadata.ValidationModelName = propertyName; + } + + private string? ReadPropertyNameFrom(IReadOnlyList attributes) + { + return attributes == null + ? throw new ArgumentNullException(nameof(attributes)) + : (attributes.OfType().FirstOrDefault()?.Name); + } +} diff --git a/src/Mvc/Mvc.Core/src/ModelBinding/Metadata/ValidationMetadata.cs b/src/Mvc/Mvc.Core/src/ModelBinding/Metadata/ValidationMetadata.cs index 107fa122302b..29205c645420 100644 --- a/src/Mvc/Mvc.Core/src/ModelBinding/Metadata/ValidationMetadata.cs +++ b/src/Mvc/Mvc.Core/src/ModelBinding/Metadata/ValidationMetadata.cs @@ -53,4 +53,9 @@ public class ValidationMetadata /// Gets or sets a value that determines if validators can be constructed using metadata on properties. /// internal bool PropertyHasValidators { get; set; } + + /// + /// Gets or sets a model name that will be used in . + /// + public string? ValidationModelName { get; set; } } diff --git a/src/Mvc/Mvc.Core/src/ModelBinding/Validation/DefaultComplexObjectValidationStrategy.cs b/src/Mvc/Mvc.Core/src/ModelBinding/Validation/DefaultComplexObjectValidationStrategy.cs index 858291ab7e93..49b9f250da82 100644 --- a/src/Mvc/Mvc.Core/src/ModelBinding/Validation/DefaultComplexObjectValidationStrategy.cs +++ b/src/Mvc/Mvc.Core/src/ModelBinding/Validation/DefaultComplexObjectValidationStrategy.cs @@ -107,7 +107,7 @@ public bool MoveNext() else { var property = _properties[_index - _parameters.Count]; - var propertyName = property.BinderModelName ?? property.PropertyName; + var propertyName = property.ValidationModelName ?? property.BinderModelName ?? property.PropertyName; var key = ModelNames.CreatePropertyModelName(_key, propertyName); if (_model == null) diff --git a/src/Mvc/Mvc.Core/src/MvcOptions.cs b/src/Mvc/Mvc.Core/src/MvcOptions.cs index 737904e00f6f..7fabee036ca2 100644 --- a/src/Mvc/Mvc.Core/src/MvcOptions.cs +++ b/src/Mvc/Mvc.Core/src/MvcOptions.cs @@ -164,6 +164,12 @@ public int MaxModelValidationErrors } } + /// + /// Gets or sets the flag which causes the be produces based on the ModelName + /// obttained by XXXX */*. by default. + /// + public bool RespectModelNameInValidationErrors { get; set; } + /// /// Gets a list of s used by this application. /// diff --git a/src/Mvc/Mvc.Core/src/PublicAPI.Unshipped.txt b/src/Mvc/Mvc.Core/src/PublicAPI.Unshipped.txt index 7dc5c58110bf..c67c7d732da4 100644 --- a/src/Mvc/Mvc.Core/src/PublicAPI.Unshipped.txt +++ b/src/Mvc/Mvc.Core/src/PublicAPI.Unshipped.txt @@ -1 +1,5 @@ #nullable enable +Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.ValidationMetadata.ValidationModelName.get -> string? +Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.ValidationMetadata.ValidationModelName.set -> void +Microsoft.AspNetCore.Mvc.MvcOptions.RespectModelNameInValidationErrors.get -> bool +Microsoft.AspNetCore.Mvc.MvcOptions.RespectModelNameInValidationErrors.set -> void diff --git a/src/Mvc/Mvc.NewtonsoftJson/src/DependencyInjection/NewtonsoftJsonMvcOptionsSetup.cs b/src/Mvc/Mvc.NewtonsoftJson/src/DependencyInjection/NewtonsoftJsonMvcOptionsSetup.cs index c550068530c8..2c4d54d1d5a9 100644 --- a/src/Mvc/Mvc.NewtonsoftJson/src/DependencyInjection/NewtonsoftJsonMvcOptionsSetup.cs +++ b/src/Mvc/Mvc.NewtonsoftJson/src/DependencyInjection/NewtonsoftJsonMvcOptionsSetup.cs @@ -3,10 +3,13 @@ using System; using System.Buffers; +using System.Linq; + using Microsoft.AspNetCore.JsonPatch; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Formatters; using Microsoft.AspNetCore.Mvc.ModelBinding; +using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata; using Microsoft.AspNetCore.Mvc.NewtonsoftJson; using Microsoft.Extensions.Logging; using Microsoft.Extensions.ObjectPool; @@ -88,5 +91,10 @@ public void Configure(MvcOptions options) options.ModelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(IJsonPatchDocument))); options.ModelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(JToken))); + + if (options.RespectModelNameInValidationErrors) + { + options.ModelMetadataDetailsProviders.Add(new NewtonsoftJsonMetadataProvider()); + } } } diff --git a/src/Mvc/Mvc.NewtonsoftJson/src/NewtonsoftJsonMetadataProvider.cs b/src/Mvc/Mvc.NewtonsoftJson/src/NewtonsoftJsonMetadataProvider.cs new file mode 100644 index 000000000000..f53adde18f67 --- /dev/null +++ b/src/Mvc/Mvc.NewtonsoftJson/src/NewtonsoftJsonMetadataProvider.cs @@ -0,0 +1,58 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Linq; +using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata; +using Newtonsoft.Json; + +namespace Microsoft.AspNetCore.Mvc.NewtonsoftJson; + +/// +/// An implementation of and for +/// the Newtonsoft.Json attribute classes. +/// +internal class NewtonsoftJsonMetadataProvider : IDisplayMetadataProvider, IValidationMetadataProvider +{ + /// + /// Initializes a new instance of . + /// + public NewtonsoftJsonMetadataProvider() + { } + + /// + public void CreateDisplayMetadata(DisplayMetadataProviderContext context) + { + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + + var propertyName = ReadPropertyNameFrom(context.Attributes); + + if (!string.IsNullOrEmpty(propertyName)) + { + context.DisplayMetadata.DisplayName = () => propertyName; + } + } + + /// + public void CreateValidationMetadata(ValidationMetadataProviderContext context) + { + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + + var propertyName = ReadPropertyNameFrom(context.Attributes); + + if (string.IsNullOrEmpty(propertyName)) + { + propertyName = context.Key.Name; + } + + context.ValidationMetadata.ValidationModelName = propertyName!; + } + + private static string? ReadPropertyNameFrom(IReadOnlyList attributes) + => attributes?.OfType().FirstOrDefault()?.PropertyName; +} From bb9f808bd32a88c65eadac3be8b3322ba5ff9b79 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Mon, 13 Dec 2021 13:24:46 -0800 Subject: [PATCH 02/16] Clean up --- .../src/ModelBinding/Metadata/JsonMetadataProvider.cs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/Mvc/Mvc.Core/src/ModelBinding/Metadata/JsonMetadataProvider.cs b/src/Mvc/Mvc.Core/src/ModelBinding/Metadata/JsonMetadataProvider.cs index c93f040906bd..3d8959e7993a 100644 --- a/src/Mvc/Mvc.Core/src/ModelBinding/Metadata/JsonMetadataProvider.cs +++ b/src/Mvc/Mvc.Core/src/ModelBinding/Metadata/JsonMetadataProvider.cs @@ -66,10 +66,6 @@ public void CreateValidationMetadata(ValidationMetadataProviderContext context) context.ValidationMetadata.ValidationModelName = propertyName; } - private string? ReadPropertyNameFrom(IReadOnlyList attributes) - { - return attributes == null - ? throw new ArgumentNullException(nameof(attributes)) - : (attributes.OfType().FirstOrDefault()?.Name); - } + private static string? ReadPropertyNameFrom(IReadOnlyList attributes) + => attributes?.OfType().FirstOrDefault()?.Name; } From b1aea75815c4853a11e44520f0c519135f978c1a Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Mon, 13 Dec 2021 14:21:50 -0800 Subject: [PATCH 03/16] Moving to JsonOptions --- .../Mvc.Core/src/Infrastructure/MvcCoreMvcOptionsSetup.cs | 2 +- src/Mvc/Mvc.Core/src/JsonOptions.cs | 6 ++++++ src/Mvc/Mvc.Core/src/MvcOptions.cs | 6 ------ src/Mvc/Mvc.Core/src/PublicAPI.Unshipped.txt | 4 ++-- .../DependencyInjection/NewtonsoftJsonMvcOptionsSetup.cs | 2 +- src/Mvc/Mvc.NewtonsoftJson/src/MvcNewtonsoftJsonOptions.cs | 6 ++++++ src/Mvc/Mvc.NewtonsoftJson/src/PublicAPI.Unshipped.txt | 2 ++ 7 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/Mvc/Mvc.Core/src/Infrastructure/MvcCoreMvcOptionsSetup.cs b/src/Mvc/Mvc.Core/src/Infrastructure/MvcCoreMvcOptionsSetup.cs index a8f3e6561f52..a83cb97cfe5c 100644 --- a/src/Mvc/Mvc.Core/src/Infrastructure/MvcCoreMvcOptionsSetup.cs +++ b/src/Mvc/Mvc.Core/src/Infrastructure/MvcCoreMvcOptionsSetup.cs @@ -117,7 +117,7 @@ public void PostConfigure(string name, MvcOptions options) // as part of ConfigureOptions. options.ModelMetadataDetailsProviders.Add(new HasValidatorsValidationMetadataProvider(options.ModelValidatorProviders)); - if (options.RespectModelNameInValidationErrors) + if (_jsonOptions.Value?.RespectModelNameInValidationErrors == true) { options.ModelMetadataDetailsProviders.Add(new JsonMetadataProvider(_jsonOptions.Value?.JsonSerializerOptions?.PropertyNamingPolicy)); } diff --git a/src/Mvc/Mvc.Core/src/JsonOptions.cs b/src/Mvc/Mvc.Core/src/JsonOptions.cs index d3966142f027..b11f4ee8583c 100644 --- a/src/Mvc/Mvc.Core/src/JsonOptions.cs +++ b/src/Mvc/Mvc.Core/src/JsonOptions.cs @@ -27,6 +27,12 @@ public class JsonOptions /// public bool AllowInputFormatterExceptionMessages { get; set; } = true; + /// + /// Gets or sets the flag which causes the be produces based on the ModelName + /// obtained by the */*. by default. + /// + public bool RespectModelNameInValidationErrors { get; set; } + /// /// Gets the used by and /// . diff --git a/src/Mvc/Mvc.Core/src/MvcOptions.cs b/src/Mvc/Mvc.Core/src/MvcOptions.cs index 7fabee036ca2..737904e00f6f 100644 --- a/src/Mvc/Mvc.Core/src/MvcOptions.cs +++ b/src/Mvc/Mvc.Core/src/MvcOptions.cs @@ -164,12 +164,6 @@ public int MaxModelValidationErrors } } - /// - /// Gets or sets the flag which causes the be produces based on the ModelName - /// obttained by XXXX */*. by default. - /// - public bool RespectModelNameInValidationErrors { get; set; } - /// /// Gets a list of s used by this application. /// diff --git a/src/Mvc/Mvc.Core/src/PublicAPI.Unshipped.txt b/src/Mvc/Mvc.Core/src/PublicAPI.Unshipped.txt index c67c7d732da4..99567cd1b274 100644 --- a/src/Mvc/Mvc.Core/src/PublicAPI.Unshipped.txt +++ b/src/Mvc/Mvc.Core/src/PublicAPI.Unshipped.txt @@ -1,5 +1,5 @@ #nullable enable +Microsoft.AspNetCore.Mvc.JsonOptions.RespectModelNameInValidationErrors.get -> bool +Microsoft.AspNetCore.Mvc.JsonOptions.RespectModelNameInValidationErrors.set -> void Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.ValidationMetadata.ValidationModelName.get -> string? Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.ValidationMetadata.ValidationModelName.set -> void -Microsoft.AspNetCore.Mvc.MvcOptions.RespectModelNameInValidationErrors.get -> bool -Microsoft.AspNetCore.Mvc.MvcOptions.RespectModelNameInValidationErrors.set -> void diff --git a/src/Mvc/Mvc.NewtonsoftJson/src/DependencyInjection/NewtonsoftJsonMvcOptionsSetup.cs b/src/Mvc/Mvc.NewtonsoftJson/src/DependencyInjection/NewtonsoftJsonMvcOptionsSetup.cs index 2c4d54d1d5a9..54d912c442d3 100644 --- a/src/Mvc/Mvc.NewtonsoftJson/src/DependencyInjection/NewtonsoftJsonMvcOptionsSetup.cs +++ b/src/Mvc/Mvc.NewtonsoftJson/src/DependencyInjection/NewtonsoftJsonMvcOptionsSetup.cs @@ -92,7 +92,7 @@ public void Configure(MvcOptions options) options.ModelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(IJsonPatchDocument))); options.ModelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(JToken))); - if (options.RespectModelNameInValidationErrors) + if (_jsonOptions.RespectModelNameInValidationErrors) { options.ModelMetadataDetailsProviders.Add(new NewtonsoftJsonMetadataProvider()); } diff --git a/src/Mvc/Mvc.NewtonsoftJson/src/MvcNewtonsoftJsonOptions.cs b/src/Mvc/Mvc.NewtonsoftJson/src/MvcNewtonsoftJsonOptions.cs index 5cc4091218eb..82a04afaf386 100644 --- a/src/Mvc/Mvc.NewtonsoftJson/src/MvcNewtonsoftJsonOptions.cs +++ b/src/Mvc/Mvc.NewtonsoftJson/src/MvcNewtonsoftJsonOptions.cs @@ -58,6 +58,12 @@ public class MvcNewtonsoftJsonOptions : IEnumerable /// The default value is . /// public bool ReadJsonWithRequestCulture { get; set; } + + /// + /// Gets or sets the flag which causes the be produces based on the ModelName + /// obtained by the */*. by default. + /// + public bool RespectModelNameInValidationErrors { get; set; } /// /// Gets the maximum size to buffer in memory when is not set. diff --git a/src/Mvc/Mvc.NewtonsoftJson/src/PublicAPI.Unshipped.txt b/src/Mvc/Mvc.NewtonsoftJson/src/PublicAPI.Unshipped.txt index 7dc5c58110bf..88bfcc1d447b 100644 --- a/src/Mvc/Mvc.NewtonsoftJson/src/PublicAPI.Unshipped.txt +++ b/src/Mvc/Mvc.NewtonsoftJson/src/PublicAPI.Unshipped.txt @@ -1 +1,3 @@ #nullable enable +Microsoft.AspNetCore.Mvc.MvcNewtonsoftJsonOptions.RespectModelNameInValidationErrors.get -> bool +Microsoft.AspNetCore.Mvc.MvcNewtonsoftJsonOptions.RespectModelNameInValidationErrors.set -> void From 03f2ea00e1a6473db2a73cd902422e2103f3a47c Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Mon, 13 Dec 2021 14:51:29 -0800 Subject: [PATCH 04/16] Moving from abstract to virtual --- src/Mvc/Mvc.Abstractions/src/ModelBinding/ModelMetadata.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mvc/Mvc.Abstractions/src/ModelBinding/ModelMetadata.cs b/src/Mvc/Mvc.Abstractions/src/ModelBinding/ModelMetadata.cs index 64ff920ba92f..5cca320fa1c5 100644 --- a/src/Mvc/Mvc.Abstractions/src/ModelBinding/ModelMetadata.cs +++ b/src/Mvc/Mvc.Abstractions/src/ModelBinding/ModelMetadata.cs @@ -501,7 +501,7 @@ internal IReadOnlyDictionary BoundConstructorPrope /// /// Gets the name of a model, if specified explicitly, to be used on /// - internal abstract string? ValidationModelName { get; } + internal virtual string? ValidationModelName { get; } /// /// Throws if the ModelMetadata is for a record type with validation on properties. From 9ee5ee84d1a51e55bb961f6bbf6ba6fdd23405b6 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Mon, 20 Dec 2021 14:25:08 -0800 Subject: [PATCH 05/16] Removing from JSonOptions --- .../Infrastructure/MvcCoreMvcOptionsSetup.cs | 7 ----- src/Mvc/Mvc.Core/src/JsonOptions.cs | 6 ---- .../Metadata/JsonMetadataProvider.cs | 30 ++++++++++++------- src/Mvc/Mvc.Core/src/PublicAPI.Unshipped.txt | 7 +++-- .../NewtonsoftJsonMvcOptionsSetup.cs | 6 +--- .../src/MvcNewtonsoftJsonOptions.cs | 6 ---- .../src/NewtonsoftJsonMetadataProvider.cs | 29 ++++++++++++++++-- .../src/PublicAPI.Unshipped.txt | 7 +++-- 8 files changed, 56 insertions(+), 42 deletions(-) diff --git a/src/Mvc/Mvc.Core/src/Infrastructure/MvcCoreMvcOptionsSetup.cs b/src/Mvc/Mvc.Core/src/Infrastructure/MvcCoreMvcOptionsSetup.cs index a83cb97cfe5c..1f317510be39 100644 --- a/src/Mvc/Mvc.Core/src/Infrastructure/MvcCoreMvcOptionsSetup.cs +++ b/src/Mvc/Mvc.Core/src/Infrastructure/MvcCoreMvcOptionsSetup.cs @@ -116,11 +116,6 @@ public void PostConfigure(string name, MvcOptions options) // This should ensure it appears later than all of the details provider registered by MVC and user configured details provider registered // as part of ConfigureOptions. options.ModelMetadataDetailsProviders.Add(new HasValidatorsValidationMetadataProvider(options.ModelValidatorProviders)); - - if (_jsonOptions.Value?.RespectModelNameInValidationErrors == true) - { - options.ModelMetadataDetailsProviders.Add(new JsonMetadataProvider(_jsonOptions.Value?.JsonSerializerOptions?.PropertyNamingPolicy)); - } } internal static void ConfigureAdditionalModelMetadataDetailsProviders(IList modelMetadataDetailsProviders) @@ -151,7 +146,5 @@ internal static void ConfigureAdditionalModelMetadataDetailsProviders(IList public bool AllowInputFormatterExceptionMessages { get; set; } = true; - /// - /// Gets or sets the flag which causes the be produces based on the ModelName - /// obtained by the */*. by default. - /// - public bool RespectModelNameInValidationErrors { get; set; } - /// /// Gets the used by and /// . diff --git a/src/Mvc/Mvc.Core/src/ModelBinding/Metadata/JsonMetadataProvider.cs b/src/Mvc/Mvc.Core/src/ModelBinding/Metadata/JsonMetadataProvider.cs index 3d8959e7993a..cb62d25fe06d 100644 --- a/src/Mvc/Mvc.Core/src/ModelBinding/Metadata/JsonMetadataProvider.cs +++ b/src/Mvc/Mvc.Core/src/ModelBinding/Metadata/JsonMetadataProvider.cs @@ -13,24 +13,32 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata; /// An implementation of and for /// the System.Text.Json.Serialization attribute classes. /// -internal class JsonMetadataProvider : IDisplayMetadataProvider, IValidationMetadataProvider +public class JsonMetadataProvider : IDisplayMetadataProvider, IValidationMetadataProvider { - private readonly JsonNamingPolicy? _jsonNamingPolicy; + private readonly JsonNamingPolicy _jsonNamingPolicy = JsonNamingPolicy.CamelCase; /// - /// Creates a new . + /// Creates a new with the default /// public JsonMetadataProvider() - : this(JsonNamingPolicy.CamelCase) - { - } + { } /// - /// Creates a new with an optional + /// Creates a new with an optional /// - /// The to be used to convert the property name - public JsonMetadataProvider(JsonNamingPolicy? jsonNamingPolicy) - => _jsonNamingPolicy = jsonNamingPolicy; + /// The to be used to configure the metadata provider. + public JsonMetadataProvider(JsonOptions jsonOptions) + { + if (jsonOptions == null) + { + throw new ArgumentNullException(nameof(jsonOptions)); + } + + if (jsonOptions.JsonSerializerOptions?.PropertyNamingPolicy != null) + { + _jsonNamingPolicy = jsonOptions.JsonSerializerOptions.PropertyNamingPolicy; + } + } /// public void CreateDisplayMetadata(DisplayMetadataProviderContext context) @@ -60,7 +68,7 @@ public void CreateValidationMetadata(ValidationMetadataProviderContext context) if (string.IsNullOrEmpty(propertyName)) { - propertyName = _jsonNamingPolicy?.ConvertName(context.Key.Name!) ?? context.Key.Name; + propertyName = _jsonNamingPolicy.ConvertName(context.Key.Name!); } context.ValidationMetadata.ValidationModelName = propertyName; diff --git a/src/Mvc/Mvc.Core/src/PublicAPI.Unshipped.txt b/src/Mvc/Mvc.Core/src/PublicAPI.Unshipped.txt index 99567cd1b274..a5fea77a59d0 100644 --- a/src/Mvc/Mvc.Core/src/PublicAPI.Unshipped.txt +++ b/src/Mvc/Mvc.Core/src/PublicAPI.Unshipped.txt @@ -1,5 +1,8 @@ #nullable enable -Microsoft.AspNetCore.Mvc.JsonOptions.RespectModelNameInValidationErrors.get -> bool -Microsoft.AspNetCore.Mvc.JsonOptions.RespectModelNameInValidationErrors.set -> void +Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.JsonMetadataProvider +Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.JsonMetadataProvider.CreateDisplayMetadata(Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.DisplayMetadataProviderContext! context) -> void +Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.JsonMetadataProvider.CreateValidationMetadata(Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.ValidationMetadataProviderContext! context) -> void +Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.JsonMetadataProvider.JsonMetadataProvider() -> void +Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.JsonMetadataProvider.JsonMetadataProvider(Microsoft.AspNetCore.Mvc.JsonOptions! jsonOptions) -> void Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.ValidationMetadata.ValidationModelName.get -> string? Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.ValidationMetadata.ValidationModelName.set -> void diff --git a/src/Mvc/Mvc.NewtonsoftJson/src/DependencyInjection/NewtonsoftJsonMvcOptionsSetup.cs b/src/Mvc/Mvc.NewtonsoftJson/src/DependencyInjection/NewtonsoftJsonMvcOptionsSetup.cs index 54d912c442d3..295a97bb1c19 100644 --- a/src/Mvc/Mvc.NewtonsoftJson/src/DependencyInjection/NewtonsoftJsonMvcOptionsSetup.cs +++ b/src/Mvc/Mvc.NewtonsoftJson/src/DependencyInjection/NewtonsoftJsonMvcOptionsSetup.cs @@ -91,10 +91,6 @@ public void Configure(MvcOptions options) options.ModelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(IJsonPatchDocument))); options.ModelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(JToken))); - - if (_jsonOptions.RespectModelNameInValidationErrors) - { - options.ModelMetadataDetailsProviders.Add(new NewtonsoftJsonMetadataProvider()); - } + options.ModelMetadataDetailsProviders.Add(new NewtonsoftJsonMetadataProvider(_jsonOptions)); } } diff --git a/src/Mvc/Mvc.NewtonsoftJson/src/MvcNewtonsoftJsonOptions.cs b/src/Mvc/Mvc.NewtonsoftJson/src/MvcNewtonsoftJsonOptions.cs index 82a04afaf386..5cc4091218eb 100644 --- a/src/Mvc/Mvc.NewtonsoftJson/src/MvcNewtonsoftJsonOptions.cs +++ b/src/Mvc/Mvc.NewtonsoftJson/src/MvcNewtonsoftJsonOptions.cs @@ -58,12 +58,6 @@ public class MvcNewtonsoftJsonOptions : IEnumerable /// The default value is . /// public bool ReadJsonWithRequestCulture { get; set; } - - /// - /// Gets or sets the flag which causes the be produces based on the ModelName - /// obtained by the */*. by default. - /// - public bool RespectModelNameInValidationErrors { get; set; } /// /// Gets the maximum size to buffer in memory when is not set. diff --git a/src/Mvc/Mvc.NewtonsoftJson/src/NewtonsoftJsonMetadataProvider.cs b/src/Mvc/Mvc.NewtonsoftJson/src/NewtonsoftJsonMetadataProvider.cs index f53adde18f67..f9a5994b304b 100644 --- a/src/Mvc/Mvc.NewtonsoftJson/src/NewtonsoftJsonMetadataProvider.cs +++ b/src/Mvc/Mvc.NewtonsoftJson/src/NewtonsoftJsonMetadataProvider.cs @@ -2,8 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Linq; + using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata; + using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; namespace Microsoft.AspNetCore.Mvc.NewtonsoftJson; @@ -11,14 +14,34 @@ namespace Microsoft.AspNetCore.Mvc.NewtonsoftJson; /// An implementation of and for /// the Newtonsoft.Json attribute classes. /// -internal class NewtonsoftJsonMetadataProvider : IDisplayMetadataProvider, IValidationMetadataProvider +public class NewtonsoftJsonMetadataProvider : IDisplayMetadataProvider, IValidationMetadataProvider { + private readonly NamingStrategy _jsonNamingPolicy = new CamelCaseNamingStrategy(); + /// - /// Initializes a new instance of . + /// Creates a new with the default /// public NewtonsoftJsonMetadataProvider() { } + /// + /// Initializes a new instance of with an optional + /// + /// The to be used to configure the metadata provider. + public NewtonsoftJsonMetadataProvider(MvcNewtonsoftJsonOptions jsonOptions) + { + if (jsonOptions == null) + { + throw new ArgumentNullException(nameof(jsonOptions)); + } + + var contractResolver = jsonOptions.SerializerSettings?.ContractResolver as DefaultContractResolver; + if (contractResolver?.NamingStrategy != null) + { + _jsonNamingPolicy = contractResolver.NamingStrategy; + } + } + /// public void CreateDisplayMetadata(DisplayMetadataProviderContext context) { @@ -47,7 +70,7 @@ public void CreateValidationMetadata(ValidationMetadataProviderContext context) if (string.IsNullOrEmpty(propertyName)) { - propertyName = context.Key.Name; + propertyName = _jsonNamingPolicy.GetPropertyName(context.Key.Name!, false); } context.ValidationMetadata.ValidationModelName = propertyName!; diff --git a/src/Mvc/Mvc.NewtonsoftJson/src/PublicAPI.Unshipped.txt b/src/Mvc/Mvc.NewtonsoftJson/src/PublicAPI.Unshipped.txt index 88bfcc1d447b..2f0d467f6bf4 100644 --- a/src/Mvc/Mvc.NewtonsoftJson/src/PublicAPI.Unshipped.txt +++ b/src/Mvc/Mvc.NewtonsoftJson/src/PublicAPI.Unshipped.txt @@ -1,3 +1,6 @@ #nullable enable -Microsoft.AspNetCore.Mvc.MvcNewtonsoftJsonOptions.RespectModelNameInValidationErrors.get -> bool -Microsoft.AspNetCore.Mvc.MvcNewtonsoftJsonOptions.RespectModelNameInValidationErrors.set -> void +Microsoft.AspNetCore.Mvc.NewtonsoftJson.NewtonsoftJsonMetadataProvider +Microsoft.AspNetCore.Mvc.NewtonsoftJson.NewtonsoftJsonMetadataProvider.CreateDisplayMetadata(Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.DisplayMetadataProviderContext! context) -> void +Microsoft.AspNetCore.Mvc.NewtonsoftJson.NewtonsoftJsonMetadataProvider.CreateValidationMetadata(Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.ValidationMetadataProviderContext! context) -> void +Microsoft.AspNetCore.Mvc.NewtonsoftJson.NewtonsoftJsonMetadataProvider.NewtonsoftJsonMetadataProvider() -> void +Microsoft.AspNetCore.Mvc.NewtonsoftJson.NewtonsoftJsonMetadataProvider.NewtonsoftJsonMetadataProvider(Microsoft.AspNetCore.Mvc.MvcNewtonsoftJsonOptions! jsonOptions) -> void From f38f57c89c680f3d458b506fab1beb14e69ed070 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Mon, 20 Dec 2021 15:18:34 -0800 Subject: [PATCH 06/16] Updating constructor --- .../Metadata/JsonMetadataProvider.cs | 18 +++++++------- src/Mvc/Mvc.Core/src/PublicAPI.Unshipped.txt | 2 +- .../src/NewtonsoftJsonMetadataProvider.cs | 24 ++++++++++++++----- .../src/PublicAPI.Unshipped.txt | 2 +- 4 files changed, 28 insertions(+), 18 deletions(-) diff --git a/src/Mvc/Mvc.Core/src/ModelBinding/Metadata/JsonMetadataProvider.cs b/src/Mvc/Mvc.Core/src/ModelBinding/Metadata/JsonMetadataProvider.cs index cb62d25fe06d..ce2f97bc8658 100644 --- a/src/Mvc/Mvc.Core/src/ModelBinding/Metadata/JsonMetadataProvider.cs +++ b/src/Mvc/Mvc.Core/src/ModelBinding/Metadata/JsonMetadataProvider.cs @@ -15,29 +15,27 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata; /// public class JsonMetadataProvider : IDisplayMetadataProvider, IValidationMetadataProvider { - private readonly JsonNamingPolicy _jsonNamingPolicy = JsonNamingPolicy.CamelCase; + private readonly JsonNamingPolicy _jsonNamingPolicy; /// /// Creates a new with the default /// public JsonMetadataProvider() + : this(JsonNamingPolicy.CamelCase) { } /// - /// Creates a new with an optional + /// Creates a new with an optional /// - /// The to be used to configure the metadata provider. - public JsonMetadataProvider(JsonOptions jsonOptions) + /// The to be used to configure the metadata provider. + public JsonMetadataProvider(JsonNamingPolicy namingPolicy) { - if (jsonOptions == null) + if (namingPolicy == null) { - throw new ArgumentNullException(nameof(jsonOptions)); + throw new ArgumentNullException(nameof(namingPolicy)); } - if (jsonOptions.JsonSerializerOptions?.PropertyNamingPolicy != null) - { - _jsonNamingPolicy = jsonOptions.JsonSerializerOptions.PropertyNamingPolicy; - } + _jsonNamingPolicy = namingPolicy; } /// diff --git a/src/Mvc/Mvc.Core/src/PublicAPI.Unshipped.txt b/src/Mvc/Mvc.Core/src/PublicAPI.Unshipped.txt index a5fea77a59d0..1d35b1bb104f 100644 --- a/src/Mvc/Mvc.Core/src/PublicAPI.Unshipped.txt +++ b/src/Mvc/Mvc.Core/src/PublicAPI.Unshipped.txt @@ -3,6 +3,6 @@ Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.JsonMetadataProvider Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.JsonMetadataProvider.CreateDisplayMetadata(Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.DisplayMetadataProviderContext! context) -> void Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.JsonMetadataProvider.CreateValidationMetadata(Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.ValidationMetadataProviderContext! context) -> void Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.JsonMetadataProvider.JsonMetadataProvider() -> void -Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.JsonMetadataProvider.JsonMetadataProvider(Microsoft.AspNetCore.Mvc.JsonOptions! jsonOptions) -> void +Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.JsonMetadataProvider.JsonMetadataProvider(System.Text.Json.JsonNamingPolicy! namingPolicy) -> void Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.ValidationMetadata.ValidationModelName.get -> string? Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.ValidationMetadata.ValidationModelName.set -> void diff --git a/src/Mvc/Mvc.NewtonsoftJson/src/NewtonsoftJsonMetadataProvider.cs b/src/Mvc/Mvc.NewtonsoftJson/src/NewtonsoftJsonMetadataProvider.cs index f9a5994b304b..be90e356fd73 100644 --- a/src/Mvc/Mvc.NewtonsoftJson/src/NewtonsoftJsonMetadataProvider.cs +++ b/src/Mvc/Mvc.NewtonsoftJson/src/NewtonsoftJsonMetadataProvider.cs @@ -16,19 +16,34 @@ namespace Microsoft.AspNetCore.Mvc.NewtonsoftJson; /// public class NewtonsoftJsonMetadataProvider : IDisplayMetadataProvider, IValidationMetadataProvider { - private readonly NamingStrategy _jsonNamingPolicy = new CamelCaseNamingStrategy(); + private readonly NamingStrategy _jsonNamingPolicy; /// /// Creates a new with the default /// public NewtonsoftJsonMetadataProvider() + : this(new CamelCaseNamingStrategy()) { } + /// + /// Initializes a new instance of with an optional + /// + /// The to be used to configure the metadata provider. + public NewtonsoftJsonMetadataProvider(NamingStrategy namingStrategy) + { + if (namingStrategy == null) + { + throw new ArgumentNullException(nameof(namingStrategy)); + } + + _jsonNamingPolicy = namingStrategy; + } + /// /// Initializes a new instance of with an optional /// /// The to be used to configure the metadata provider. - public NewtonsoftJsonMetadataProvider(MvcNewtonsoftJsonOptions jsonOptions) + internal NewtonsoftJsonMetadataProvider(MvcNewtonsoftJsonOptions jsonOptions) { if (jsonOptions == null) { @@ -36,10 +51,7 @@ public NewtonsoftJsonMetadataProvider(MvcNewtonsoftJsonOptions jsonOptions) } var contractResolver = jsonOptions.SerializerSettings?.ContractResolver as DefaultContractResolver; - if (contractResolver?.NamingStrategy != null) - { - _jsonNamingPolicy = contractResolver.NamingStrategy; - } + _jsonNamingPolicy = contractResolver?.NamingStrategy != null ? contractResolver.NamingStrategy : new CamelCaseNamingStrategy(); } /// diff --git a/src/Mvc/Mvc.NewtonsoftJson/src/PublicAPI.Unshipped.txt b/src/Mvc/Mvc.NewtonsoftJson/src/PublicAPI.Unshipped.txt index 2f0d467f6bf4..a1b975886027 100644 --- a/src/Mvc/Mvc.NewtonsoftJson/src/PublicAPI.Unshipped.txt +++ b/src/Mvc/Mvc.NewtonsoftJson/src/PublicAPI.Unshipped.txt @@ -3,4 +3,4 @@ Microsoft.AspNetCore.Mvc.NewtonsoftJson.NewtonsoftJsonMetadataProvider Microsoft.AspNetCore.Mvc.NewtonsoftJson.NewtonsoftJsonMetadataProvider.CreateDisplayMetadata(Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.DisplayMetadataProviderContext! context) -> void Microsoft.AspNetCore.Mvc.NewtonsoftJson.NewtonsoftJsonMetadataProvider.CreateValidationMetadata(Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.ValidationMetadataProviderContext! context) -> void Microsoft.AspNetCore.Mvc.NewtonsoftJson.NewtonsoftJsonMetadataProvider.NewtonsoftJsonMetadataProvider() -> void -Microsoft.AspNetCore.Mvc.NewtonsoftJson.NewtonsoftJsonMetadataProvider.NewtonsoftJsonMetadataProvider(Microsoft.AspNetCore.Mvc.MvcNewtonsoftJsonOptions! jsonOptions) -> void +Microsoft.AspNetCore.Mvc.NewtonsoftJson.NewtonsoftJsonMetadataProvider.NewtonsoftJsonMetadataProvider(Newtonsoft.Json.Serialization.NamingStrategy! namingStrategy) -> void From 845a6683da0b10fd0d5dfa9a820d4a17df22b9b0 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Mon, 3 Jan 2022 11:09:28 -0800 Subject: [PATCH 07/16] Removing from default --- .../src/DependencyInjection/NewtonsoftJsonMvcOptionsSetup.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Mvc/Mvc.NewtonsoftJson/src/DependencyInjection/NewtonsoftJsonMvcOptionsSetup.cs b/src/Mvc/Mvc.NewtonsoftJson/src/DependencyInjection/NewtonsoftJsonMvcOptionsSetup.cs index 295a97bb1c19..667ce1e43f2f 100644 --- a/src/Mvc/Mvc.NewtonsoftJson/src/DependencyInjection/NewtonsoftJsonMvcOptionsSetup.cs +++ b/src/Mvc/Mvc.NewtonsoftJson/src/DependencyInjection/NewtonsoftJsonMvcOptionsSetup.cs @@ -91,6 +91,5 @@ public void Configure(MvcOptions options) options.ModelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(IJsonPatchDocument))); options.ModelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(JToken))); - options.ModelMetadataDetailsProviders.Add(new NewtonsoftJsonMetadataProvider(_jsonOptions)); } } From 921a2135b5d1df19797245e582cb3d67b4ca9161 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Mon, 3 Jan 2022 11:09:59 -0800 Subject: [PATCH 08/16] Renaming as suggested during API Review --- ...aProvider.cs => NewtonsoftJsonValidationMetadataProvider.cs} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/Mvc/Mvc.NewtonsoftJson/src/{NewtonsoftJsonMetadataProvider.cs => NewtonsoftJsonValidationMetadataProvider.cs} (96%) diff --git a/src/Mvc/Mvc.NewtonsoftJson/src/NewtonsoftJsonMetadataProvider.cs b/src/Mvc/Mvc.NewtonsoftJson/src/NewtonsoftJsonValidationMetadataProvider.cs similarity index 96% rename from src/Mvc/Mvc.NewtonsoftJson/src/NewtonsoftJsonMetadataProvider.cs rename to src/Mvc/Mvc.NewtonsoftJson/src/NewtonsoftJsonValidationMetadataProvider.cs index be90e356fd73..ec926a7777d4 100644 --- a/src/Mvc/Mvc.NewtonsoftJson/src/NewtonsoftJsonMetadataProvider.cs +++ b/src/Mvc/Mvc.NewtonsoftJson/src/NewtonsoftJsonValidationMetadataProvider.cs @@ -14,7 +14,7 @@ namespace Microsoft.AspNetCore.Mvc.NewtonsoftJson; /// An implementation of and for /// the Newtonsoft.Json attribute classes. /// -public class NewtonsoftJsonMetadataProvider : IDisplayMetadataProvider, IValidationMetadataProvider +public sealed class NewtonsoftJsonValidationMetadataProvider : IDisplayMetadataProvider, IValidationMetadataProvider { private readonly NamingStrategy _jsonNamingPolicy; From cf15c79cff569e54ba1c7b847b099ee886cca657 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Mon, 3 Jan 2022 11:12:05 -0800 Subject: [PATCH 09/16] Renaming as suggested during API Review --- ...aProvider.cs => SystemTextJsonValidationMetadataProvider.cs} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/Mvc/Mvc.Core/src/ModelBinding/Metadata/{JsonMetadataProvider.cs => SystemTextJsonValidationMetadataProvider.cs} (95%) diff --git a/src/Mvc/Mvc.Core/src/ModelBinding/Metadata/JsonMetadataProvider.cs b/src/Mvc/Mvc.Core/src/ModelBinding/Metadata/SystemTextJsonValidationMetadataProvider.cs similarity index 95% rename from src/Mvc/Mvc.Core/src/ModelBinding/Metadata/JsonMetadataProvider.cs rename to src/Mvc/Mvc.Core/src/ModelBinding/Metadata/SystemTextJsonValidationMetadataProvider.cs index ce2f97bc8658..b5a31c6c73fd 100644 --- a/src/Mvc/Mvc.Core/src/ModelBinding/Metadata/JsonMetadataProvider.cs +++ b/src/Mvc/Mvc.Core/src/ModelBinding/Metadata/SystemTextJsonValidationMetadataProvider.cs @@ -13,7 +13,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata; /// An implementation of and for /// the System.Text.Json.Serialization attribute classes. /// -public class JsonMetadataProvider : IDisplayMetadataProvider, IValidationMetadataProvider +public class SystemTextJsonValidationMetadataProvider : IDisplayMetadataProvider, IValidationMetadataProvider { private readonly JsonNamingPolicy _jsonNamingPolicy; From 973fac1844596248419b63a733a95f1e4b12e836 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Mon, 3 Jan 2022 11:19:29 -0800 Subject: [PATCH 10/16] Updating api --- ...ystemTextJsonValidationMetadataProvider.cs | 8 +++---- src/Mvc/Mvc.Core/src/PublicAPI.Unshipped.txt | 10 ++++---- ...ewtonsoftJsonValidationMetadataProvider.cs | 23 ++++--------------- .../src/PublicAPI.Unshipped.txt | 10 ++++---- 4 files changed, 18 insertions(+), 33 deletions(-) diff --git a/src/Mvc/Mvc.Core/src/ModelBinding/Metadata/SystemTextJsonValidationMetadataProvider.cs b/src/Mvc/Mvc.Core/src/ModelBinding/Metadata/SystemTextJsonValidationMetadataProvider.cs index b5a31c6c73fd..4a5c343bbebb 100644 --- a/src/Mvc/Mvc.Core/src/ModelBinding/Metadata/SystemTextJsonValidationMetadataProvider.cs +++ b/src/Mvc/Mvc.Core/src/ModelBinding/Metadata/SystemTextJsonValidationMetadataProvider.cs @@ -18,17 +18,17 @@ public class SystemTextJsonValidationMetadataProvider : IDisplayMetadataProvider private readonly JsonNamingPolicy _jsonNamingPolicy; /// - /// Creates a new with the default + /// Creates a new with the default /// - public JsonMetadataProvider() + public SystemTextJsonValidationMetadataProvider() : this(JsonNamingPolicy.CamelCase) { } /// - /// Creates a new with an optional + /// Creates a new with an optional /// /// The to be used to configure the metadata provider. - public JsonMetadataProvider(JsonNamingPolicy namingPolicy) + public SystemTextJsonValidationMetadataProvider(JsonNamingPolicy namingPolicy) { if (namingPolicy == null) { diff --git a/src/Mvc/Mvc.Core/src/PublicAPI.Unshipped.txt b/src/Mvc/Mvc.Core/src/PublicAPI.Unshipped.txt index 1d35b1bb104f..d326535ca4d1 100644 --- a/src/Mvc/Mvc.Core/src/PublicAPI.Unshipped.txt +++ b/src/Mvc/Mvc.Core/src/PublicAPI.Unshipped.txt @@ -1,8 +1,8 @@ #nullable enable -Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.JsonMetadataProvider -Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.JsonMetadataProvider.CreateDisplayMetadata(Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.DisplayMetadataProviderContext! context) -> void -Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.JsonMetadataProvider.CreateValidationMetadata(Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.ValidationMetadataProviderContext! context) -> void -Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.JsonMetadataProvider.JsonMetadataProvider() -> void -Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.JsonMetadataProvider.JsonMetadataProvider(System.Text.Json.JsonNamingPolicy! namingPolicy) -> void +Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.SystemTextJsonValidationMetadataProvider +Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.SystemTextJsonValidationMetadataProvider.CreateDisplayMetadata(Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.DisplayMetadataProviderContext! context) -> void +Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.SystemTextJsonValidationMetadataProvider.CreateValidationMetadata(Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.ValidationMetadataProviderContext! context) -> void +Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.SystemTextJsonValidationMetadataProvider.SystemTextJsonValidationMetadataProvider() -> void +Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.SystemTextJsonValidationMetadataProvider.SystemTextJsonValidationMetadataProvider(System.Text.Json.JsonNamingPolicy! namingPolicy) -> void Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.ValidationMetadata.ValidationModelName.get -> string? Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.ValidationMetadata.ValidationModelName.set -> void diff --git a/src/Mvc/Mvc.NewtonsoftJson/src/NewtonsoftJsonValidationMetadataProvider.cs b/src/Mvc/Mvc.NewtonsoftJson/src/NewtonsoftJsonValidationMetadataProvider.cs index ec926a7777d4..0407624fef4b 100644 --- a/src/Mvc/Mvc.NewtonsoftJson/src/NewtonsoftJsonValidationMetadataProvider.cs +++ b/src/Mvc/Mvc.NewtonsoftJson/src/NewtonsoftJsonValidationMetadataProvider.cs @@ -19,17 +19,17 @@ public sealed class NewtonsoftJsonValidationMetadataProvider : IDisplayMetadataP private readonly NamingStrategy _jsonNamingPolicy; /// - /// Creates a new with the default + /// Creates a new with the default /// - public NewtonsoftJsonMetadataProvider() + public NewtonsoftJsonValidationMetadataProvider() : this(new CamelCaseNamingStrategy()) { } /// - /// Initializes a new instance of with an optional + /// Initializes a new instance of with an optional /// /// The to be used to configure the metadata provider. - public NewtonsoftJsonMetadataProvider(NamingStrategy namingStrategy) + public NewtonsoftJsonValidationMetadataProvider(NamingStrategy namingStrategy) { if (namingStrategy == null) { @@ -39,21 +39,6 @@ public NewtonsoftJsonMetadataProvider(NamingStrategy namingStrategy) _jsonNamingPolicy = namingStrategy; } - /// - /// Initializes a new instance of with an optional - /// - /// The to be used to configure the metadata provider. - internal NewtonsoftJsonMetadataProvider(MvcNewtonsoftJsonOptions jsonOptions) - { - if (jsonOptions == null) - { - throw new ArgumentNullException(nameof(jsonOptions)); - } - - var contractResolver = jsonOptions.SerializerSettings?.ContractResolver as DefaultContractResolver; - _jsonNamingPolicy = contractResolver?.NamingStrategy != null ? contractResolver.NamingStrategy : new CamelCaseNamingStrategy(); - } - /// public void CreateDisplayMetadata(DisplayMetadataProviderContext context) { diff --git a/src/Mvc/Mvc.NewtonsoftJson/src/PublicAPI.Unshipped.txt b/src/Mvc/Mvc.NewtonsoftJson/src/PublicAPI.Unshipped.txt index a1b975886027..bd9df5e8e8a9 100644 --- a/src/Mvc/Mvc.NewtonsoftJson/src/PublicAPI.Unshipped.txt +++ b/src/Mvc/Mvc.NewtonsoftJson/src/PublicAPI.Unshipped.txt @@ -1,6 +1,6 @@ #nullable enable -Microsoft.AspNetCore.Mvc.NewtonsoftJson.NewtonsoftJsonMetadataProvider -Microsoft.AspNetCore.Mvc.NewtonsoftJson.NewtonsoftJsonMetadataProvider.CreateDisplayMetadata(Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.DisplayMetadataProviderContext! context) -> void -Microsoft.AspNetCore.Mvc.NewtonsoftJson.NewtonsoftJsonMetadataProvider.CreateValidationMetadata(Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.ValidationMetadataProviderContext! context) -> void -Microsoft.AspNetCore.Mvc.NewtonsoftJson.NewtonsoftJsonMetadataProvider.NewtonsoftJsonMetadataProvider() -> void -Microsoft.AspNetCore.Mvc.NewtonsoftJson.NewtonsoftJsonMetadataProvider.NewtonsoftJsonMetadataProvider(Newtonsoft.Json.Serialization.NamingStrategy! namingStrategy) -> void +Microsoft.AspNetCore.Mvc.NewtonsoftJson.NewtonsoftJsonValidationMetadataProvider +Microsoft.AspNetCore.Mvc.NewtonsoftJson.NewtonsoftJsonValidationMetadataProvider.CreateDisplayMetadata(Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.DisplayMetadataProviderContext! context) -> void +Microsoft.AspNetCore.Mvc.NewtonsoftJson.NewtonsoftJsonValidationMetadataProvider.CreateValidationMetadata(Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.ValidationMetadataProviderContext! context) -> void +Microsoft.AspNetCore.Mvc.NewtonsoftJson.NewtonsoftJsonValidationMetadataProvider.NewtonsoftJsonValidationMetadataProvider() -> void +Microsoft.AspNetCore.Mvc.NewtonsoftJson.NewtonsoftJsonValidationMetadataProvider.NewtonsoftJsonValidationMetadataProvider(Newtonsoft.Json.Serialization.NamingStrategy! namingStrategy) -> void From 2aab9cdcc5fa89803fc8c332330e8bffb3a24803 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Mon, 3 Jan 2022 13:52:32 -0800 Subject: [PATCH 11/16] Update src/Mvc/Mvc.Core/src/ModelBinding/Metadata/SystemTextJsonValidationMetadataProvider.cs Co-authored-by: Pranav K --- .../Metadata/SystemTextJsonValidationMetadataProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mvc/Mvc.Core/src/ModelBinding/Metadata/SystemTextJsonValidationMetadataProvider.cs b/src/Mvc/Mvc.Core/src/ModelBinding/Metadata/SystemTextJsonValidationMetadataProvider.cs index 4a5c343bbebb..d34e6f48aa42 100644 --- a/src/Mvc/Mvc.Core/src/ModelBinding/Metadata/SystemTextJsonValidationMetadataProvider.cs +++ b/src/Mvc/Mvc.Core/src/ModelBinding/Metadata/SystemTextJsonValidationMetadataProvider.cs @@ -13,7 +13,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata; /// An implementation of and for /// the System.Text.Json.Serialization attribute classes. /// -public class SystemTextJsonValidationMetadataProvider : IDisplayMetadataProvider, IValidationMetadataProvider +public sealed class SystemTextJsonValidationMetadataProvider : IDisplayMetadataProvider, IValidationMetadataProvider { private readonly JsonNamingPolicy _jsonNamingPolicy; From 3e5dc816263e6a5398b2f2ae5d00a3c3acd8b180 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Mon, 3 Jan 2022 13:53:31 -0800 Subject: [PATCH 12/16] Adding SystemTextJsonValidationMetadataProvider unit test --- ...mTextJsonValidationMetadataProviderTest.cs | 92 +++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 src/Mvc/Mvc.Core/test/ModelBinding/Metadata/SystemTextJsonValidationMetadataProviderTest.cs diff --git a/src/Mvc/Mvc.Core/test/ModelBinding/Metadata/SystemTextJsonValidationMetadataProviderTest.cs b/src/Mvc/Mvc.Core/test/ModelBinding/Metadata/SystemTextJsonValidationMetadataProviderTest.cs new file mode 100644 index 000000000000..69af7b9cbc99 --- /dev/null +++ b/src/Mvc/Mvc.Core/test/ModelBinding/Metadata/SystemTextJsonValidationMetadataProviderTest.cs @@ -0,0 +1,92 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata; + +public class SystemTextJsonValidationMetadataProviderTest +{ + [Fact] + public void CreateValidationMetadata_SetValidationPropertyName_WithJsonPropertyNameAttribute() + { + var metadataProvider = new SystemTextJsonValidationMetadataProvider(); + var propertyName = "sample-data"; + + var key = ModelMetadataIdentity.ForProperty(typeof(SampleTestClass).GetProperty(nameof(SampleTestClass.NonAttributesProperty)), typeof(int), typeof(SampleTestClass)); + var modelAttributes = new ModelAttributes(Array.Empty(), new[] { new JsonPropertyNameAttribute(propertyName) }, Array.Empty()); + var context = new ValidationMetadataProviderContext(key, modelAttributes); + + // Act + metadataProvider.CreateValidationMetadata(context); + + // Assert + Assert.NotNull(context.ValidationMetadata.ValidationModelName); + Assert.Equal(propertyName, context.ValidationMetadata.ValidationModelName); + } + + [Fact] + public void CreateValidationMetadata_SetValidationPropertyName_CamelCaseWithDefaultNamingPolicy() + { + var metadataProvider = new SystemTextJsonValidationMetadataProvider(); + var propertyName = nameof(SampleTestClass.NonAttributesProperty); + + var key = ModelMetadataIdentity.ForProperty(typeof(SampleTestClass).GetProperty(propertyName), typeof(int), typeof(SampleTestClass)); + var modelAttributes = new ModelAttributes(Array.Empty(), Array.Empty(), Array.Empty()); + var context = new ValidationMetadataProviderContext(key, modelAttributes); + + // Act + metadataProvider.CreateValidationMetadata(context); + + // Assert + Assert.NotNull(context.ValidationMetadata.ValidationModelName); + Assert.Equal(JsonNamingPolicy.CamelCase.ConvertName(propertyName), context.ValidationMetadata.ValidationModelName); + } + + [Theory] + [MemberData(nameof(NamingPolicies))] + public void CreateValidationMetadata_SetValidationPropertyName_WithJsonNamingPolicy(JsonNamingPolicy namingPolicy) + { + var metadataProvider = new SystemTextJsonValidationMetadataProvider(namingPolicy); + var propertyName = nameof(SampleTestClass.NonAttributesProperty); + + var key = ModelMetadataIdentity.ForProperty(typeof(SampleTestClass).GetProperty(propertyName), typeof(int), typeof(SampleTestClass)); + var modelAttributes = new ModelAttributes(Array.Empty(), Array.Empty(), Array.Empty()); + var context = new ValidationMetadataProviderContext(key, modelAttributes); + + // Act + metadataProvider.CreateValidationMetadata(context); + + // Assert + Assert.NotNull(context.ValidationMetadata.ValidationModelName); + Assert.Equal(namingPolicy.ConvertName(propertyName), context.ValidationMetadata.ValidationModelName); + } + + public static TheoryData NamingPolicies + { + get + { + return new TheoryData + { + UpperCaseJsonNamingPolicy.Instance, + JsonNamingPolicy.CamelCase + }; + } + } + + public class UpperCaseJsonNamingPolicy : System.Text.Json.JsonNamingPolicy + { + public static JsonNamingPolicy Instance = new UpperCaseJsonNamingPolicy(); + + public override string ConvertName(string name) + { + return name?.ToUpperInvariant(); + } + } + + public class SampleTestClass + { + public int NonAttributesProperty { get; set; } + } +} From aba9851ed2fdb32f379d384002e1b59c53e4458e Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Mon, 3 Jan 2022 14:25:42 -0800 Subject: [PATCH 13/16] Adding unittest --- ...mTextJsonValidationMetadataProviderTest.cs | 8 +- .../NewtonsoftJsonMvcOptionsSetup.cs | 4 +- ...ewtonsoftJsonValidationMetadataProvider.cs | 2 - ...nsoftJsonValidationMetadataProviderTest.cs | 90 +++++++++++++++++++ 4 files changed, 95 insertions(+), 9 deletions(-) create mode 100644 src/Mvc/Mvc.NewtonsoftJson/test/NewtonsoftJsonValidationMetadataProviderTest.cs diff --git a/src/Mvc/Mvc.Core/test/ModelBinding/Metadata/SystemTextJsonValidationMetadataProviderTest.cs b/src/Mvc/Mvc.Core/test/ModelBinding/Metadata/SystemTextJsonValidationMetadataProviderTest.cs index 69af7b9cbc99..11964d793636 100644 --- a/src/Mvc/Mvc.Core/test/ModelBinding/Metadata/SystemTextJsonValidationMetadataProviderTest.cs +++ b/src/Mvc/Mvc.Core/test/ModelBinding/Metadata/SystemTextJsonValidationMetadataProviderTest.cs @@ -14,7 +14,7 @@ public void CreateValidationMetadata_SetValidationPropertyName_WithJsonPropertyN var metadataProvider = new SystemTextJsonValidationMetadataProvider(); var propertyName = "sample-data"; - var key = ModelMetadataIdentity.ForProperty(typeof(SampleTestClass).GetProperty(nameof(SampleTestClass.NonAttributesProperty)), typeof(int), typeof(SampleTestClass)); + var key = ModelMetadataIdentity.ForProperty(typeof(SampleTestClass).GetProperty(nameof(SampleTestClass.NoAttributesProperty)), typeof(int), typeof(SampleTestClass)); var modelAttributes = new ModelAttributes(Array.Empty(), new[] { new JsonPropertyNameAttribute(propertyName) }, Array.Empty()); var context = new ValidationMetadataProviderContext(key, modelAttributes); @@ -30,7 +30,7 @@ public void CreateValidationMetadata_SetValidationPropertyName_WithJsonPropertyN public void CreateValidationMetadata_SetValidationPropertyName_CamelCaseWithDefaultNamingPolicy() { var metadataProvider = new SystemTextJsonValidationMetadataProvider(); - var propertyName = nameof(SampleTestClass.NonAttributesProperty); + var propertyName = nameof(SampleTestClass.NoAttributesProperty); var key = ModelMetadataIdentity.ForProperty(typeof(SampleTestClass).GetProperty(propertyName), typeof(int), typeof(SampleTestClass)); var modelAttributes = new ModelAttributes(Array.Empty(), Array.Empty(), Array.Empty()); @@ -49,7 +49,7 @@ public void CreateValidationMetadata_SetValidationPropertyName_CamelCaseWithDefa public void CreateValidationMetadata_SetValidationPropertyName_WithJsonNamingPolicy(JsonNamingPolicy namingPolicy) { var metadataProvider = new SystemTextJsonValidationMetadataProvider(namingPolicy); - var propertyName = nameof(SampleTestClass.NonAttributesProperty); + var propertyName = nameof(SampleTestClass.NoAttributesProperty); var key = ModelMetadataIdentity.ForProperty(typeof(SampleTestClass).GetProperty(propertyName), typeof(int), typeof(SampleTestClass)); var modelAttributes = new ModelAttributes(Array.Empty(), Array.Empty(), Array.Empty()); @@ -87,6 +87,6 @@ public override string ConvertName(string name) public class SampleTestClass { - public int NonAttributesProperty { get; set; } + public int NoAttributesProperty { get; set; } } } diff --git a/src/Mvc/Mvc.NewtonsoftJson/src/DependencyInjection/NewtonsoftJsonMvcOptionsSetup.cs b/src/Mvc/Mvc.NewtonsoftJson/src/DependencyInjection/NewtonsoftJsonMvcOptionsSetup.cs index 667ce1e43f2f..b984c624e2d8 100644 --- a/src/Mvc/Mvc.NewtonsoftJson/src/DependencyInjection/NewtonsoftJsonMvcOptionsSetup.cs +++ b/src/Mvc/Mvc.NewtonsoftJson/src/DependencyInjection/NewtonsoftJsonMvcOptionsSetup.cs @@ -1,19 +1,17 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; using System.Buffers; -using System.Linq; using Microsoft.AspNetCore.JsonPatch; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Formatters; using Microsoft.AspNetCore.Mvc.ModelBinding; -using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata; using Microsoft.AspNetCore.Mvc.NewtonsoftJson; using Microsoft.Extensions.Logging; using Microsoft.Extensions.ObjectPool; using Microsoft.Extensions.Options; + using Newtonsoft.Json.Linq; namespace Microsoft.Extensions.DependencyInjection; diff --git a/src/Mvc/Mvc.NewtonsoftJson/src/NewtonsoftJsonValidationMetadataProvider.cs b/src/Mvc/Mvc.NewtonsoftJson/src/NewtonsoftJsonValidationMetadataProvider.cs index 0407624fef4b..1fe66d72c685 100644 --- a/src/Mvc/Mvc.NewtonsoftJson/src/NewtonsoftJsonValidationMetadataProvider.cs +++ b/src/Mvc/Mvc.NewtonsoftJson/src/NewtonsoftJsonValidationMetadataProvider.cs @@ -2,9 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Linq; - using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata; - using Newtonsoft.Json; using Newtonsoft.Json.Serialization; diff --git a/src/Mvc/Mvc.NewtonsoftJson/test/NewtonsoftJsonValidationMetadataProviderTest.cs b/src/Mvc/Mvc.NewtonsoftJson/test/NewtonsoftJsonValidationMetadataProviderTest.cs new file mode 100644 index 000000000000..d635e04691d5 --- /dev/null +++ b/src/Mvc/Mvc.NewtonsoftJson/test/NewtonsoftJsonValidationMetadataProviderTest.cs @@ -0,0 +1,90 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + + +namespace Microsoft.AspNetCore.Mvc.NewtonsoftJson; + +using Microsoft.AspNetCore.Mvc.ModelBinding; +using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +public class NewtonsoftJsonValidationMetadataProviderTest +{ + [Fact] + public void CreateValidationMetadata_SetValidationPropertyName_WithJsonPropertyNameAttribute() + { + var metadataProvider = new NewtonsoftJsonValidationMetadataProvider(); + var propertyName = "sample-data"; + + var key = ModelMetadataIdentity.ForProperty(typeof(SampleTestClass).GetProperty(nameof(SampleTestClass.NoAttributesProperty)), typeof(int), typeof(SampleTestClass)); + var modelAttributes = new ModelAttributes(Array.Empty(), new[] { new JsonPropertyAttribute() { PropertyName = propertyName } }, Array.Empty()); + var context = new ValidationMetadataProviderContext(key, modelAttributes); + + // Act + metadataProvider.CreateValidationMetadata(context); + + // Assert + Assert.NotNull(context.ValidationMetadata.ValidationModelName); + Assert.Equal(propertyName, context.ValidationMetadata.ValidationModelName); + } + + [Fact] + public void CreateValidationMetadata_SetValidationPropertyName_CamelCaseWithDefaultNamingPolicy() + { + var metadataProvider = new NewtonsoftJsonValidationMetadataProvider(); + var propertyName = nameof(SampleTestClass.NoAttributesProperty); + + var key = ModelMetadataIdentity.ForProperty(typeof(SampleTestClass).GetProperty(propertyName), typeof(int), typeof(SampleTestClass)); + var modelAttributes = new ModelAttributes(Array.Empty(), Array.Empty(), Array.Empty()); + var context = new ValidationMetadataProviderContext(key, modelAttributes); + + // Act + metadataProvider.CreateValidationMetadata(context); + + // Assert + Assert.NotNull(context.ValidationMetadata.ValidationModelName); + Assert.Equal(new CamelCaseNamingStrategy().GetPropertyName(propertyName, false), context.ValidationMetadata.ValidationModelName); + } + + [Theory] + [MemberData(nameof(NamingPolicies))] + public void CreateValidationMetadata_SetValidationPropertyName_WithJsonNamingPolicy(NamingStrategy namingStrategy) + { + var metadataProvider = new NewtonsoftJsonValidationMetadataProvider(namingStrategy); + var propertyName = nameof(SampleTestClass.NoAttributesProperty); + + var key = ModelMetadataIdentity.ForProperty(typeof(SampleTestClass).GetProperty(propertyName), typeof(int), typeof(SampleTestClass)); + var modelAttributes = new ModelAttributes(Array.Empty(), Array.Empty(), Array.Empty()); + var context = new ValidationMetadataProviderContext(key, modelAttributes); + + // Act + metadataProvider.CreateValidationMetadata(context); + + // Assert + Assert.NotNull(context.ValidationMetadata.ValidationModelName); + Assert.Equal(namingStrategy.GetPropertyName(propertyName, false), context.ValidationMetadata.ValidationModelName); + } + + public static TheoryData NamingPolicies + { + get + { + return new TheoryData + { + new UpperCaseJsonNamingPolicy(), + new CamelCaseNamingStrategy() + }; + } + } + + public class UpperCaseJsonNamingPolicy : NamingStrategy + { + protected override string ResolvePropertyName(string name) => name?.ToUpperInvariant(); + } + + public class SampleTestClass + { + public int NoAttributesProperty { get; set; } + } +} From 9777b5240d1de097796c8f2b29b3e03f8d284f20 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Mon, 3 Jan 2022 15:11:14 -0800 Subject: [PATCH 14/16] Adding unit test --- ...aultComplexObjectValidationStrategyTest.cs | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/src/Mvc/Mvc.Core/test/ModelBinding/Validation/DefaultComplexObjectValidationStrategyTest.cs b/src/Mvc/Mvc.Core/test/ModelBinding/Validation/DefaultComplexObjectValidationStrategyTest.cs index 868b005f71b3..370e00ae3cab 100644 --- a/src/Mvc/Mvc.Core/test/ModelBinding/Validation/DefaultComplexObjectValidationStrategyTest.cs +++ b/src/Mvc/Mvc.Core/test/ModelBinding/Validation/DefaultComplexObjectValidationStrategyTest.cs @@ -4,6 +4,10 @@ using System; using System.Collections.Generic; using System.Linq; +using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata; + +using Moq; + using Xunit; namespace Microsoft.AspNetCore.Mvc.ModelBinding.Validation; @@ -50,6 +54,46 @@ public void GetChildren_ReturnsExpectedElements() }); } + [Fact] + public void GetChildren_ReturnsExpectedElements_WithValidationModelName() + { + // Arrange + var model = new Person() + { + Age = 23, + Id = 1, + Name = "Joey", + }; + + var metadata = TestModelMetadataProvider.CreateDefaultProvider(new List { new TestValidationModelNameProvider() }).GetMetadataForType(typeof(Person)); + var strategy = DefaultComplexObjectValidationStrategy.Instance; + + // Act + var enumerator = strategy.GetChildren(metadata, "prefix", model); + + // Assert + Assert.Collection( + BufferEntries(enumerator).OrderBy(e => e.Key), + entry => + { + Assert.Equal("prefix.AGE", entry.Key); + Assert.Equal(23, entry.Model); + Assert.Same(metadata.Properties["Age"], entry.Metadata); + }, + entry => + { + Assert.Equal("prefix.ID", entry.Key); + Assert.Equal(1, entry.Model); + Assert.Same(metadata.Properties["Id"], entry.Metadata); + }, + entry => + { + Assert.Equal("prefix.NAME", entry.Key); + Assert.Equal("Joey", entry.Model); + Assert.Same(metadata.Properties["Name"], entry.Metadata); + }); + } + [Fact] public void GetChildren_SetsModelNull_IfContainerNull() { @@ -154,4 +198,9 @@ public LazyPerson(string input) public string Name => _string.Substring(3, 5); } + + private class TestValidationModelNameProvider : IValidationMetadataProvider + { + public void CreateValidationMetadata(ValidationMetadataProviderContext context) => context.ValidationMetadata.ValidationModelName = context.Key.Name?.ToUpperInvariant(); + } } From 6536fe71f13b1a8e2ead12ef546412dd4124611f Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Mon, 3 Jan 2022 15:43:26 -0800 Subject: [PATCH 15/16] Fixing coding style --- .../Validation/DefaultComplexObjectValidationStrategyTest.cs | 3 ++- .../src/DependencyInjection/NewtonsoftJsonMvcOptionsSetup.cs | 2 -- .../test/NewtonsoftJsonValidationMetadataProviderTest.cs | 5 ++--- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/Mvc/Mvc.Core/test/ModelBinding/Validation/DefaultComplexObjectValidationStrategyTest.cs b/src/Mvc/Mvc.Core/test/ModelBinding/Validation/DefaultComplexObjectValidationStrategyTest.cs index 87d23b988c88..913adafead44 100644 --- a/src/Mvc/Mvc.Core/test/ModelBinding/Validation/DefaultComplexObjectValidationStrategyTest.cs +++ b/src/Mvc/Mvc.Core/test/ModelBinding/Validation/DefaultComplexObjectValidationStrategyTest.cs @@ -194,6 +194,7 @@ public LazyPerson(string input) private class TestValidationModelNameProvider : IValidationMetadataProvider { - public void CreateValidationMetadata(ValidationMetadataProviderContext context) => context.ValidationMetadata.ValidationModelName = context.Key.Name?.ToUpperInvariant(); + public void CreateValidationMetadata(ValidationMetadataProviderContext context) + => context.ValidationMetadata.ValidationModelName = context.Key.Name?.ToUpperInvariant(); } } diff --git a/src/Mvc/Mvc.NewtonsoftJson/src/DependencyInjection/NewtonsoftJsonMvcOptionsSetup.cs b/src/Mvc/Mvc.NewtonsoftJson/src/DependencyInjection/NewtonsoftJsonMvcOptionsSetup.cs index b984c624e2d8..6279aa0c010b 100644 --- a/src/Mvc/Mvc.NewtonsoftJson/src/DependencyInjection/NewtonsoftJsonMvcOptionsSetup.cs +++ b/src/Mvc/Mvc.NewtonsoftJson/src/DependencyInjection/NewtonsoftJsonMvcOptionsSetup.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Buffers; - using Microsoft.AspNetCore.JsonPatch; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Formatters; @@ -11,7 +10,6 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.ObjectPool; using Microsoft.Extensions.Options; - using Newtonsoft.Json.Linq; namespace Microsoft.Extensions.DependencyInjection; diff --git a/src/Mvc/Mvc.NewtonsoftJson/test/NewtonsoftJsonValidationMetadataProviderTest.cs b/src/Mvc/Mvc.NewtonsoftJson/test/NewtonsoftJsonValidationMetadataProviderTest.cs index d635e04691d5..b231c097ffeb 100644 --- a/src/Mvc/Mvc.NewtonsoftJson/test/NewtonsoftJsonValidationMetadataProviderTest.cs +++ b/src/Mvc/Mvc.NewtonsoftJson/test/NewtonsoftJsonValidationMetadataProviderTest.cs @@ -1,14 +1,13 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. - -namespace Microsoft.AspNetCore.Mvc.NewtonsoftJson; - using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; +namespace Microsoft.AspNetCore.Mvc.NewtonsoftJson; + public class NewtonsoftJsonValidationMetadataProviderTest { [Fact] From e0dcf08b5e7a081ee646068a64406993968b711c Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Mon, 3 Jan 2022 16:40:24 -0800 Subject: [PATCH 16/16] Fix formatting --- .../test/NewtonsoftJsonValidationMetadataProviderTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mvc/Mvc.NewtonsoftJson/test/NewtonsoftJsonValidationMetadataProviderTest.cs b/src/Mvc/Mvc.NewtonsoftJson/test/NewtonsoftJsonValidationMetadataProviderTest.cs index b231c097ffeb..bdf7ff613981 100644 --- a/src/Mvc/Mvc.NewtonsoftJson/test/NewtonsoftJsonValidationMetadataProviderTest.cs +++ b/src/Mvc/Mvc.NewtonsoftJson/test/NewtonsoftJsonValidationMetadataProviderTest.cs @@ -78,7 +78,7 @@ public static TheoryData NamingPolicies } public class UpperCaseJsonNamingPolicy : NamingStrategy - { + { protected override string ResolvePropertyName(string name) => name?.ToUpperInvariant(); }