From 9cc7ddf03801e81b797dee9f49d4773c7d3398b9 Mon Sep 17 00:00:00 2001 From: maurei Date: Wed, 30 Sep 2020 09:54:19 +0200 Subject: [PATCH 01/27] feat: JsonApiModelValidatorProvider --- .../ApplicationBuilderExtensions.cs | 3 +++ .../JsonApiModelValidationProvider.cs | 27 +++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 src/JsonApiDotNetCore/Configuration/JsonApiModelValidationProvider.cs diff --git a/src/JsonApiDotNetCore/Configuration/ApplicationBuilderExtensions.cs b/src/JsonApiDotNetCore/Configuration/ApplicationBuilderExtensions.cs index 0d4d20f59c..839b345594 100644 --- a/src/JsonApiDotNetCore/Configuration/ApplicationBuilderExtensions.cs +++ b/src/JsonApiDotNetCore/Configuration/ApplicationBuilderExtensions.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using JsonApiDotNetCore.Middleware; using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; @@ -39,6 +40,8 @@ public static void UseJsonApi(this IApplicationBuilder builder) var routingConvention = builder.ApplicationServices.GetRequiredService(); options.Conventions.Insert(0, routingConvention); + + options.ModelValidatorProviders[0] = new JsonApiModelValidationProvider(options.ModelValidatorProviders[0]); }; builder.UseMiddleware(); diff --git a/src/JsonApiDotNetCore/Configuration/JsonApiModelValidationProvider.cs b/src/JsonApiDotNetCore/Configuration/JsonApiModelValidationProvider.cs new file mode 100644 index 0000000000..3e10b7d1ca --- /dev/null +++ b/src/JsonApiDotNetCore/Configuration/JsonApiModelValidationProvider.cs @@ -0,0 +1,27 @@ + +using System; +using System.Collections.Generic; +using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; + +namespace JsonApiDotNetCore.Configuration +{ + public sealed class JsonApiModelValidationProvider : IMetadataBasedModelValidatorProvider + { + private readonly IMetadataBasedModelValidatorProvider _internalModelValidatorProvider; + + public JsonApiModelValidationProvider(IModelValidatorProvider internalModelValidatorProvider) + { + _internalModelValidatorProvider = internalModelValidatorProvider as IMetadataBasedModelValidatorProvider ?? throw new ArgumentNullException(nameof(internalModelValidatorProvider)); + } + + public void CreateValidators(ModelValidatorProviderContext context) + { + _internalModelValidatorProvider.CreateValidators(context); + } + + public bool HasValidators(Type modelType, IList validatorMetadata) + { + return _internalModelValidatorProvider.HasValidators(modelType, validatorMetadata); + } + } +} From 345ef67635a91804959a22bb30a1cb1940454ca4 Mon Sep 17 00:00:00 2001 From: maurei Date: Wed, 30 Sep 2020 15:14:21 +0200 Subject: [PATCH 02/27] feat: working approach --- .../ApplicationBuilderExtensions.cs | 86 +++++++++++++++++ .../JsonApiModelValidationProvider.cs | 95 ++++++++++++++++++- ...e.cs => PartialPatchCompatibleRequired.cs} | 5 +- .../Serialization/RequestDeserializer.cs | 5 +- .../ModelStateValidationTests.cs | 44 ++++----- .../SystemDirectoriesController.cs | 2 + .../ModelStateValidation/SystemDirectory.cs | 4 +- .../ModelStateValidation/SystemFile.cs | 4 +- 8 files changed, 213 insertions(+), 32 deletions(-) rename src/JsonApiDotNetCore/Resources/Annotations/{IsRequiredAttribute.cs => PartialPatchCompatibleRequired.cs} (97%) diff --git a/src/JsonApiDotNetCore/Configuration/ApplicationBuilderExtensions.cs b/src/JsonApiDotNetCore/Configuration/ApplicationBuilderExtensions.cs index 839b345594..e35ca723b7 100644 --- a/src/JsonApiDotNetCore/Configuration/ApplicationBuilderExtensions.cs +++ b/src/JsonApiDotNetCore/Configuration/ApplicationBuilderExtensions.cs @@ -1,7 +1,13 @@ using System; +using System.Collections; +using System.Collections.Generic; using System.Linq; +using System.Reflection; +using System.Runtime.CompilerServices; using JsonApiDotNetCore.Middleware; using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata; using Microsoft.Extensions.DependencyInjection; namespace JsonApiDotNetCore.Configuration @@ -40,6 +46,10 @@ public static void UseJsonApi(this IApplicationBuilder builder) var routingConvention = builder.ApplicationServices.GetRequiredService(); options.Conventions.Insert(0, routingConvention); + + // var interceptor = new MetadataDetailsProviderListInterceptor(options.ModelMetadataDetailsProviders); + // var property = typeof(MvcOptions).GetField("k__BackingField", BindingFlags.Instance | BindingFlags.NonPublic); + // property.SetValue(options, interceptor); options.ModelValidatorProviders[0] = new JsonApiModelValidationProvider(options.ModelValidatorProviders[0]); }; @@ -47,4 +57,80 @@ public static void UseJsonApi(this IApplicationBuilder builder) builder.UseMiddleware(); } } + + internal sealed class MetadataDetailsProviderListInterceptor : IList + { + private readonly IList _list; + + public MetadataDetailsProviderListInterceptor(IList list) + { + _list = list; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Add(IMetadataDetailsProvider item) + { + + if (item is IBindingMetadataProvider && item is IDisplayMetadataProvider && item is IValidationMetadataProvider) + { + _list.Add(new JsonApiMetadataProvider(item)); + } + + _list.Add(item); + } + + public void Clear() + { + _list.Clear(); + } + + public bool Contains(IMetadataDetailsProvider item) + { + return _list.Contains(item); + } + + public void CopyTo(IMetadataDetailsProvider[] array, int arrayIndex) + { + _list.CopyTo(array, arrayIndex); + } + + public bool Remove(IMetadataDetailsProvider item) + { + return _list.Remove(item); + } + + public int Count => _list.Count; + public bool IsReadOnly => _list.IsReadOnly; + + public IEnumerator GetEnumerator() + { + return _list.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public int IndexOf(IMetadataDetailsProvider item) + { + return _list.IndexOf(item); + } + + public void Insert(int index, IMetadataDetailsProvider item) + { + _list.Insert(index, item); + } + + public void RemoveAt(int index) + { + _list.RemoveAt(index); + } + + public IMetadataDetailsProvider this[int index] + { + get => _list[index]; + set => _list[index] = value; + } + } } diff --git a/src/JsonApiDotNetCore/Configuration/JsonApiModelValidationProvider.cs b/src/JsonApiDotNetCore/Configuration/JsonApiModelValidationProvider.cs index 3e10b7d1ca..e754064e16 100644 --- a/src/JsonApiDotNetCore/Configuration/JsonApiModelValidationProvider.cs +++ b/src/JsonApiDotNetCore/Configuration/JsonApiModelValidationProvider.cs @@ -1,6 +1,12 @@ using System; using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Reflection; +using JsonApiDotNetCore.Resources.Annotations; +using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata; using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; namespace JsonApiDotNetCore.Configuration @@ -16,12 +22,97 @@ public JsonApiModelValidationProvider(IModelValidatorProvider internalModelValid public void CreateValidators(ModelValidatorProviderContext context) { - _internalModelValidatorProvider.CreateValidators(context); + var itemsToRemove = new List(); + + foreach (var item in context.Results) + { + if (item.ValidatorMetadata.GetType() == typeof(RequiredAttribute)) + { + // var interceptor = new MetadataDetailsProviderListInterceptor(options.ModelMetadataDetailsProviders); + var property = item.GetType().GetField("k__BackingField", BindingFlags.Instance | BindingFlags.NonPublic); + property.SetValue(item, new JsonApiRequiredAttribute()); + // itemsToRemove.Add(item); + } + } + + // foreach (var item in itemsToRemove) + // { + // context.Results.Remove(item); + // } } public bool HasValidators(Type modelType, IList validatorMetadata) { - return _internalModelValidatorProvider.HasValidators(modelType, validatorMetadata); + var hasValidators = _internalModelValidatorProvider.HasValidators(modelType, validatorMetadata); + return hasValidators; + } + + + private void FuckAround(ValidationContext validationContext) + { + // var metadata = validationContext.ModelMetadata; + // var memberName = metadata.Name; + // var container = validationContext.Container; + // + // var context = new ValidationContext( + // instance: container ?? validationContext.Model ?? _emptyValidationContextInstance, + // serviceProvider: validationContext.ActionContext?.HttpContext?.RequestServices, + // items: null) + // { + // DisplayName = metadata.GetDisplayName(), + // MemberName = memberName + // }; + } + } + + public interface IInternalDataAnnotationsMetadataProvider : IBindingMetadataProvider, + IDisplayMetadataProvider, + IValidationMetadataProvider { } + + public sealed class JsonApiMetadataProvider : + IBindingMetadataProvider, + IDisplayMetadataProvider, + IValidationMetadataProvider + { + private readonly IMetadataDetailsProvider _internalProvider; + + public JsonApiMetadataProvider(IMetadataDetailsProvider internalProvider) + { + _internalProvider = internalProvider ?? throw new ArgumentNullException(nameof(internalProvider)); + } + + /// + public void CreateBindingMetadata(BindingMetadataProviderContext context) + { + ((IBindingMetadataProvider)_internalProvider).CreateBindingMetadata(context); + } + + /// + public void CreateDisplayMetadata(DisplayMetadataProviderContext context) + { + ((IDisplayMetadataProvider)_internalProvider).CreateDisplayMetadata(context); + } + + /// + public void CreateValidationMetadata(ValidationMetadataProviderContext context) + { + ((IValidationMetadataProvider)_internalProvider).CreateValidationMetadata(context); + + var validatorMedata = context.ValidationMetadata.ValidatorMetadata; + var itemsToReplaceIndices = new List(); + + for (int i = 0; i < validatorMedata.Count; i++) + { + if (validatorMedata[i] is RequiredAttribute) + { + itemsToReplaceIndices.Add(i); + } + } + + foreach (var index in itemsToReplaceIndices) + { + validatorMedata[index] = new JsonApiRequiredAttribute(); + } } } } diff --git a/src/JsonApiDotNetCore/Resources/Annotations/IsRequiredAttribute.cs b/src/JsonApiDotNetCore/Resources/Annotations/PartialPatchCompatibleRequired.cs similarity index 97% rename from src/JsonApiDotNetCore/Resources/Annotations/IsRequiredAttribute.cs rename to src/JsonApiDotNetCore/Resources/Annotations/PartialPatchCompatibleRequired.cs index 0d031aa7d2..717731fa0c 100644 --- a/src/JsonApiDotNetCore/Resources/Annotations/IsRequiredAttribute.cs +++ b/src/JsonApiDotNetCore/Resources/Annotations/PartialPatchCompatibleRequired.cs @@ -5,6 +5,7 @@ using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Middleware; using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.Extensions.DependencyInjection; namespace JsonApiDotNetCore.Resources.Annotations @@ -12,7 +13,7 @@ namespace JsonApiDotNetCore.Resources.Annotations /// /// Used with model state validation as a replacement for the built-in to support partial updates. /// - public sealed class IsRequiredAttribute : RequiredAttribute + public sealed class JsonApiRequiredAttribute : RequiredAttribute { private const string _isSelfReferencingResourceKey = "JsonApiDotNetCore_IsSelfReferencingResource"; @@ -31,7 +32,7 @@ protected override ValidationResult IsValid(object value, ValidationContext vali { return ValidationResult.Success; } - + return base.IsValid(value, validationContext); } diff --git a/src/JsonApiDotNetCore/Serialization/RequestDeserializer.cs b/src/JsonApiDotNetCore/Serialization/RequestDeserializer.cs index cc8250ba57..357b52c6d4 100644 --- a/src/JsonApiDotNetCore/Serialization/RequestDeserializer.cs +++ b/src/JsonApiDotNetCore/Serialization/RequestDeserializer.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; using System.Net.Http; using System.Reflection; using JsonApiDotNetCore.Configuration; @@ -77,10 +78,10 @@ protected override IIdentifiable SetAttributes(IIdentifiable resource, IDictiona { foreach (AttrAttribute attr in attributes) { - if (attr.Property.GetCustomAttribute() != null) + if (attr.Property.GetCustomAttribute() != null) { bool disableValidator = attributeValues == null || !attributeValues.ContainsKey(attr.PublicName); - + if (disableValidator) { _httpContextAccessor.HttpContext.DisableRequiredValidator(attr.Property.Name, resource.GetType().Name); diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ModelStateValidation/ModelStateValidationTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ModelStateValidation/ModelStateValidationTests.cs index e85da62d5b..d060a7febe 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ModelStateValidation/ModelStateValidationTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ModelStateValidation/ModelStateValidationTests.cs @@ -518,28 +518,28 @@ await _testContext.RunOnDatabaseAsync(async dbContext => }, relationships = new Dictionary { - ["subdirectories"] = new - { - data = new[] - { - new - { - type = "systemDirectories", - id = otherSubdirectory.StringId - } - } - }, - ["files"] = new - { - data = new[] - { - new - { - type = "systemFiles", - id = otherFile.StringId - } - } - }, + // ["subdirectories"] = new + // { + // data = new[] + // { + // new + // { + // type = "systemDirectories", + // id = otherSubdirectory.StringId + // } + // } + // }, + // ["files"] = new + // { + // data = new[] + // { + // new + // { + // type = "systemFiles", + // id = otherFile.StringId + // } + // } + // }, ["parent"] = new { data = new diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ModelStateValidation/SystemDirectoriesController.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ModelStateValidation/SystemDirectoriesController.cs index 5228903c5d..1d65f4ef49 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ModelStateValidation/SystemDirectoriesController.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ModelStateValidation/SystemDirectoriesController.cs @@ -1,7 +1,9 @@ using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Controllers; using JsonApiDotNetCore.Services; +using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ModelStateValidation { diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ModelStateValidation/SystemDirectory.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ModelStateValidation/SystemDirectory.cs index e60b4bd864..ddc036d27f 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ModelStateValidation/SystemDirectory.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ModelStateValidation/SystemDirectory.cs @@ -8,12 +8,12 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ModelStateValidation public sealed class SystemDirectory : Identifiable { [Attr] - [IsRequired] + [Required] [RegularExpression(@"^[\w\s]+$")] public string Name { get; set; } [Attr] - [IsRequired] + [Required] public bool? IsCaseSensitive { get; set; } [Attr] diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ModelStateValidation/SystemFile.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ModelStateValidation/SystemFile.cs index bb2e27a6fc..a7890730f2 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ModelStateValidation/SystemFile.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ModelStateValidation/SystemFile.cs @@ -7,12 +7,12 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ModelStateValidation public sealed class SystemFile : Identifiable { [Attr] - [IsRequired] + [Required] [MinLength(1)] public string FileName { get; set; } [Attr] - [IsRequired] + [Required] [Range(typeof(long), "0", "9223372036854775807")] public long SizeInBytes { get; set; } } From 30b532fe3388fa2813183855c814a98b873e6a90 Mon Sep 17 00:00:00 2001 From: maurei Date: Wed, 30 Sep 2020 15:25:59 +0200 Subject: [PATCH 03/27] chore: cleanup --- .../ApplicationBuilderExtensions.cs | 89 +-------------- .../JsonApiModelValidationProvider.cs | 104 ++---------------- 2 files changed, 12 insertions(+), 181 deletions(-) diff --git a/src/JsonApiDotNetCore/Configuration/ApplicationBuilderExtensions.cs b/src/JsonApiDotNetCore/Configuration/ApplicationBuilderExtensions.cs index e35ca723b7..f3cde133dd 100644 --- a/src/JsonApiDotNetCore/Configuration/ApplicationBuilderExtensions.cs +++ b/src/JsonApiDotNetCore/Configuration/ApplicationBuilderExtensions.cs @@ -30,12 +30,13 @@ public static class ApplicationBuilderExtensions public static void UseJsonApi(this IApplicationBuilder builder) { if (builder == null) throw new ArgumentNullException(nameof(builder)); - + using var scope = builder.ApplicationServices.GetRequiredService().CreateScope(); var inverseRelationshipResolver = scope.ServiceProvider.GetRequiredService(); inverseRelationshipResolver.Resolve(); - - var jsonApiApplicationBuilder = builder.ApplicationServices.GetRequiredService(); + + var jsonApiApplicationBuilder = + builder.ApplicationServices.GetRequiredService(); jsonApiApplicationBuilder.ConfigureMvcOptions = options => { var inputFormatter = builder.ApplicationServices.GetRequiredService(); @@ -46,91 +47,11 @@ public static void UseJsonApi(this IApplicationBuilder builder) var routingConvention = builder.ApplicationServices.GetRequiredService(); options.Conventions.Insert(0, routingConvention); - - // var interceptor = new MetadataDetailsProviderListInterceptor(options.ModelMetadataDetailsProviders); - // var property = typeof(MvcOptions).GetField("k__BackingField", BindingFlags.Instance | BindingFlags.NonPublic); - // property.SetValue(options, interceptor); - options.ModelValidatorProviders[0] = new JsonApiModelValidationProvider(options.ModelValidatorProviders[0]); + options.ModelValidatorProviders.Insert(0, new JsonApiModelValidationProvider()); }; builder.UseMiddleware(); } } - - internal sealed class MetadataDetailsProviderListInterceptor : IList - { - private readonly IList _list; - - public MetadataDetailsProviderListInterceptor(IList list) - { - _list = list; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Add(IMetadataDetailsProvider item) - { - - if (item is IBindingMetadataProvider && item is IDisplayMetadataProvider && item is IValidationMetadataProvider) - { - _list.Add(new JsonApiMetadataProvider(item)); - } - - _list.Add(item); - } - - public void Clear() - { - _list.Clear(); - } - - public bool Contains(IMetadataDetailsProvider item) - { - return _list.Contains(item); - } - - public void CopyTo(IMetadataDetailsProvider[] array, int arrayIndex) - { - _list.CopyTo(array, arrayIndex); - } - - public bool Remove(IMetadataDetailsProvider item) - { - return _list.Remove(item); - } - - public int Count => _list.Count; - public bool IsReadOnly => _list.IsReadOnly; - - public IEnumerator GetEnumerator() - { - return _list.GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - public int IndexOf(IMetadataDetailsProvider item) - { - return _list.IndexOf(item); - } - - public void Insert(int index, IMetadataDetailsProvider item) - { - _list.Insert(index, item); - } - - public void RemoveAt(int index) - { - _list.RemoveAt(index); - } - - public IMetadataDetailsProvider this[int index] - { - get => _list[index]; - set => _list[index] = value; - } - } } diff --git a/src/JsonApiDotNetCore/Configuration/JsonApiModelValidationProvider.cs b/src/JsonApiDotNetCore/Configuration/JsonApiModelValidationProvider.cs index e754064e16..0348a0b617 100644 --- a/src/JsonApiDotNetCore/Configuration/JsonApiModelValidationProvider.cs +++ b/src/JsonApiDotNetCore/Configuration/JsonApiModelValidationProvider.cs @@ -1,118 +1,28 @@ - -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; using System.ComponentModel.DataAnnotations; -using System.Linq; using System.Reflection; using JsonApiDotNetCore.Resources.Annotations; -using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata; using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; namespace JsonApiDotNetCore.Configuration { - public sealed class JsonApiModelValidationProvider : IMetadataBasedModelValidatorProvider + public sealed class JsonApiModelValidationProvider : IModelValidatorProvider { - private readonly IMetadataBasedModelValidatorProvider _internalModelValidatorProvider; - - public JsonApiModelValidationProvider(IModelValidatorProvider internalModelValidatorProvider) + private static readonly FieldInfo _validatorMetadataBackingField; + static JsonApiModelValidationProvider() { - _internalModelValidatorProvider = internalModelValidatorProvider as IMetadataBasedModelValidatorProvider ?? throw new ArgumentNullException(nameof(internalModelValidatorProvider)); - } + _validatorMetadataBackingField = typeof(ValidatorItem).GetField("k__BackingField", BindingFlags.Instance | BindingFlags.NonPublic); + } + public void CreateValidators(ModelValidatorProviderContext context) { - var itemsToRemove = new List(); - foreach (var item in context.Results) { if (item.ValidatorMetadata.GetType() == typeof(RequiredAttribute)) { - // var interceptor = new MetadataDetailsProviderListInterceptor(options.ModelMetadataDetailsProviders); - var property = item.GetType().GetField("k__BackingField", BindingFlags.Instance | BindingFlags.NonPublic); - property.SetValue(item, new JsonApiRequiredAttribute()); - // itemsToRemove.Add(item); + _validatorMetadataBackingField.SetValue(item, new JsonApiRequiredAttribute()); } } - - // foreach (var item in itemsToRemove) - // { - // context.Results.Remove(item); - // } - } - - public bool HasValidators(Type modelType, IList validatorMetadata) - { - var hasValidators = _internalModelValidatorProvider.HasValidators(modelType, validatorMetadata); - return hasValidators; - } - - - private void FuckAround(ValidationContext validationContext) - { - // var metadata = validationContext.ModelMetadata; - // var memberName = metadata.Name; - // var container = validationContext.Container; - // - // var context = new ValidationContext( - // instance: container ?? validationContext.Model ?? _emptyValidationContextInstance, - // serviceProvider: validationContext.ActionContext?.HttpContext?.RequestServices, - // items: null) - // { - // DisplayName = metadata.GetDisplayName(), - // MemberName = memberName - // }; - } - } - - public interface IInternalDataAnnotationsMetadataProvider : IBindingMetadataProvider, - IDisplayMetadataProvider, - IValidationMetadataProvider { } - - public sealed class JsonApiMetadataProvider : - IBindingMetadataProvider, - IDisplayMetadataProvider, - IValidationMetadataProvider - { - private readonly IMetadataDetailsProvider _internalProvider; - - public JsonApiMetadataProvider(IMetadataDetailsProvider internalProvider) - { - _internalProvider = internalProvider ?? throw new ArgumentNullException(nameof(internalProvider)); - } - - /// - public void CreateBindingMetadata(BindingMetadataProviderContext context) - { - ((IBindingMetadataProvider)_internalProvider).CreateBindingMetadata(context); - } - - /// - public void CreateDisplayMetadata(DisplayMetadataProviderContext context) - { - ((IDisplayMetadataProvider)_internalProvider).CreateDisplayMetadata(context); - } - - /// - public void CreateValidationMetadata(ValidationMetadataProviderContext context) - { - ((IValidationMetadataProvider)_internalProvider).CreateValidationMetadata(context); - - var validatorMedata = context.ValidationMetadata.ValidatorMetadata; - var itemsToReplaceIndices = new List(); - - for (int i = 0; i < validatorMedata.Count; i++) - { - if (validatorMedata[i] is RequiredAttribute) - { - itemsToReplaceIndices.Add(i); - } - } - - foreach (var index in itemsToReplaceIndices) - { - validatorMedata[index] = new JsonApiRequiredAttribute(); - } } } } From 672f975a579e65ae6de1a432cf6e9cba9ee43be4 Mon Sep 17 00:00:00 2001 From: maurei Date: Wed, 30 Sep 2020 15:27:25 +0200 Subject: [PATCH 04/27] chore: unused directives --- .../Configuration/ApplicationBuilderExtensions.cs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/JsonApiDotNetCore/Configuration/ApplicationBuilderExtensions.cs b/src/JsonApiDotNetCore/Configuration/ApplicationBuilderExtensions.cs index f3cde133dd..b25c963598 100644 --- a/src/JsonApiDotNetCore/Configuration/ApplicationBuilderExtensions.cs +++ b/src/JsonApiDotNetCore/Configuration/ApplicationBuilderExtensions.cs @@ -1,13 +1,6 @@ using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Runtime.CompilerServices; using JsonApiDotNetCore.Middleware; using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata; using Microsoft.Extensions.DependencyInjection; namespace JsonApiDotNetCore.Configuration From bdfd4285ffcadb2d9f9698b627c727edf43f64a6 Mon Sep 17 00:00:00 2001 From: maurei Date: Wed, 30 Sep 2020 15:30:49 +0200 Subject: [PATCH 05/27] chore: self review --- .../ApplicationBuilderExtensions.cs | 3 +- ...equired.cs => JsonApiRequiredAttribute.cs} | 2 +- .../ModelStateValidationTests.cs | 44 +++++++++---------- .../SystemDirectoriesController.cs | 2 - 4 files changed, 24 insertions(+), 27 deletions(-) rename src/JsonApiDotNetCore/Resources/Annotations/{PartialPatchCompatibleRequired.cs => JsonApiRequiredAttribute.cs} (99%) diff --git a/src/JsonApiDotNetCore/Configuration/ApplicationBuilderExtensions.cs b/src/JsonApiDotNetCore/Configuration/ApplicationBuilderExtensions.cs index b25c963598..a0c7100982 100644 --- a/src/JsonApiDotNetCore/Configuration/ApplicationBuilderExtensions.cs +++ b/src/JsonApiDotNetCore/Configuration/ApplicationBuilderExtensions.cs @@ -28,8 +28,7 @@ public static void UseJsonApi(this IApplicationBuilder builder) var inverseRelationshipResolver = scope.ServiceProvider.GetRequiredService(); inverseRelationshipResolver.Resolve(); - var jsonApiApplicationBuilder = - builder.ApplicationServices.GetRequiredService(); + var jsonApiApplicationBuilder = builder.ApplicationServices.GetRequiredService(); jsonApiApplicationBuilder.ConfigureMvcOptions = options => { var inputFormatter = builder.ApplicationServices.GetRequiredService(); diff --git a/src/JsonApiDotNetCore/Resources/Annotations/PartialPatchCompatibleRequired.cs b/src/JsonApiDotNetCore/Resources/Annotations/JsonApiRequiredAttribute.cs similarity index 99% rename from src/JsonApiDotNetCore/Resources/Annotations/PartialPatchCompatibleRequired.cs rename to src/JsonApiDotNetCore/Resources/Annotations/JsonApiRequiredAttribute.cs index 717731fa0c..9040ec3c3b 100644 --- a/src/JsonApiDotNetCore/Resources/Annotations/PartialPatchCompatibleRequired.cs +++ b/src/JsonApiDotNetCore/Resources/Annotations/JsonApiRequiredAttribute.cs @@ -32,7 +32,7 @@ protected override ValidationResult IsValid(object value, ValidationContext vali { return ValidationResult.Success; } - + return base.IsValid(value, validationContext); } diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ModelStateValidation/ModelStateValidationTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ModelStateValidation/ModelStateValidationTests.cs index d060a7febe..e85da62d5b 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ModelStateValidation/ModelStateValidationTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ModelStateValidation/ModelStateValidationTests.cs @@ -518,28 +518,28 @@ await _testContext.RunOnDatabaseAsync(async dbContext => }, relationships = new Dictionary { - // ["subdirectories"] = new - // { - // data = new[] - // { - // new - // { - // type = "systemDirectories", - // id = otherSubdirectory.StringId - // } - // } - // }, - // ["files"] = new - // { - // data = new[] - // { - // new - // { - // type = "systemFiles", - // id = otherFile.StringId - // } - // } - // }, + ["subdirectories"] = new + { + data = new[] + { + new + { + type = "systemDirectories", + id = otherSubdirectory.StringId + } + } + }, + ["files"] = new + { + data = new[] + { + new + { + type = "systemFiles", + id = otherFile.StringId + } + } + }, ["parent"] = new { data = new diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ModelStateValidation/SystemDirectoriesController.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ModelStateValidation/SystemDirectoriesController.cs index 1d65f4ef49..5228903c5d 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ModelStateValidation/SystemDirectoriesController.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ModelStateValidation/SystemDirectoriesController.cs @@ -1,9 +1,7 @@ using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Controllers; using JsonApiDotNetCore.Services; -using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ModelStateValidation { From b5c00f614c9006b4932c64661deab98c74c02fb8 Mon Sep 17 00:00:00 2001 From: maurei Date: Wed, 30 Sep 2020 15:41:37 +0200 Subject: [PATCH 06/27] fix: failing tests --- .../Configuration/ApplicationBuilderExtensions.cs | 2 +- .../Configuration/JsonApiModelValidationProvider.cs | 6 +++++- .../Resources/Annotations/JsonApiRequiredAttribute.cs | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/JsonApiDotNetCore/Configuration/ApplicationBuilderExtensions.cs b/src/JsonApiDotNetCore/Configuration/ApplicationBuilderExtensions.cs index a0c7100982..eb060fa47d 100644 --- a/src/JsonApiDotNetCore/Configuration/ApplicationBuilderExtensions.cs +++ b/src/JsonApiDotNetCore/Configuration/ApplicationBuilderExtensions.cs @@ -40,7 +40,7 @@ public static void UseJsonApi(this IApplicationBuilder builder) var routingConvention = builder.ApplicationServices.GetRequiredService(); options.Conventions.Insert(0, routingConvention); - options.ModelValidatorProviders.Insert(0, new JsonApiModelValidationProvider()); + options.ModelValidatorProviders.Add(new JsonApiModelValidationProvider()); }; builder.UseMiddleware(); diff --git a/src/JsonApiDotNetCore/Configuration/JsonApiModelValidationProvider.cs b/src/JsonApiDotNetCore/Configuration/JsonApiModelValidationProvider.cs index 0348a0b617..a68308d34a 100644 --- a/src/JsonApiDotNetCore/Configuration/JsonApiModelValidationProvider.cs +++ b/src/JsonApiDotNetCore/Configuration/JsonApiModelValidationProvider.cs @@ -1,3 +1,5 @@ +using System; +using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Reflection; using JsonApiDotNetCore.Resources.Annotations; @@ -5,7 +7,7 @@ namespace JsonApiDotNetCore.Configuration { - public sealed class JsonApiModelValidationProvider : IModelValidatorProvider + public sealed class JsonApiModelValidationProvider : IMetadataBasedModelValidatorProvider { private static readonly FieldInfo _validatorMetadataBackingField; static JsonApiModelValidationProvider() @@ -24,5 +26,7 @@ public void CreateValidators(ModelValidatorProviderContext context) } } } + + public bool HasValidators(Type modelType, IList validatorMetadata) => false; } } diff --git a/src/JsonApiDotNetCore/Resources/Annotations/JsonApiRequiredAttribute.cs b/src/JsonApiDotNetCore/Resources/Annotations/JsonApiRequiredAttribute.cs index 9040ec3c3b..ca88b2a3cd 100644 --- a/src/JsonApiDotNetCore/Resources/Annotations/JsonApiRequiredAttribute.cs +++ b/src/JsonApiDotNetCore/Resources/Annotations/JsonApiRequiredAttribute.cs @@ -13,7 +13,7 @@ namespace JsonApiDotNetCore.Resources.Annotations /// /// Used with model state validation as a replacement for the built-in to support partial updates. /// - public sealed class JsonApiRequiredAttribute : RequiredAttribute + internal sealed class JsonApiRequiredAttribute : RequiredAttribute { private const string _isSelfReferencingResourceKey = "JsonApiDotNetCore_IsSelfReferencingResource"; From 9e90e8c4124aee8392a8b0f45194edb0f1fe5d39 Mon Sep 17 00:00:00 2001 From: maurei Date: Wed, 30 Sep 2020 16:11:51 +0200 Subject: [PATCH 07/27] fix --- .../Configuration/JsonApiModelValidationProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/JsonApiDotNetCore/Configuration/JsonApiModelValidationProvider.cs b/src/JsonApiDotNetCore/Configuration/JsonApiModelValidationProvider.cs index a68308d34a..4dd8f7384b 100644 --- a/src/JsonApiDotNetCore/Configuration/JsonApiModelValidationProvider.cs +++ b/src/JsonApiDotNetCore/Configuration/JsonApiModelValidationProvider.cs @@ -18,7 +18,7 @@ static JsonApiModelValidationProvider() public void CreateValidators(ModelValidatorProviderContext context) { - foreach (var item in context.Results) + foreach (var item in context.Results) { if (item.ValidatorMetadata.GetType() == typeof(RequiredAttribute)) { From 38499394674f7e974a82f293f107f94a263a055c Mon Sep 17 00:00:00 2001 From: maurei Date: Wed, 30 Sep 2020 16:12:04 +0200 Subject: [PATCH 08/27] fix --- .../Configuration/JsonApiModelValidationProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/JsonApiDotNetCore/Configuration/JsonApiModelValidationProvider.cs b/src/JsonApiDotNetCore/Configuration/JsonApiModelValidationProvider.cs index 4dd8f7384b..a68308d34a 100644 --- a/src/JsonApiDotNetCore/Configuration/JsonApiModelValidationProvider.cs +++ b/src/JsonApiDotNetCore/Configuration/JsonApiModelValidationProvider.cs @@ -18,7 +18,7 @@ static JsonApiModelValidationProvider() public void CreateValidators(ModelValidatorProviderContext context) { - foreach (var item in context.Results) + foreach (var item in context.Results) { if (item.ValidatorMetadata.GetType() == typeof(RequiredAttribute)) { From 8bf11aba2dab3d5eaa4e81d0dad5cd2987828117 Mon Sep 17 00:00:00 2001 From: maurei Date: Wed, 30 Sep 2020 20:46:55 +0200 Subject: [PATCH 09/27] fix: cleanup --- .../Configuration/JsonApiModelValidationProvider.cs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/JsonApiDotNetCore/Configuration/JsonApiModelValidationProvider.cs b/src/JsonApiDotNetCore/Configuration/JsonApiModelValidationProvider.cs index a68308d34a..78ba287759 100644 --- a/src/JsonApiDotNetCore/Configuration/JsonApiModelValidationProvider.cs +++ b/src/JsonApiDotNetCore/Configuration/JsonApiModelValidationProvider.cs @@ -7,13 +7,17 @@ namespace JsonApiDotNetCore.Configuration { - public sealed class JsonApiModelValidationProvider : IMetadataBasedModelValidatorProvider + /// + /// This model validator provider does not create any validators, but is used to indirectly change the behavior of + /// the internal through the shared object. + /// + internal sealed class JsonApiModelValidationProvider : IMetadataBasedModelValidatorProvider { private static readonly FieldInfo _validatorMetadataBackingField; + static JsonApiModelValidationProvider() { - _validatorMetadataBackingField = typeof(ValidatorItem).GetField("k__BackingField", BindingFlags.Instance | BindingFlags.NonPublic); - + _validatorMetadataBackingField = typeof(ValidatorItem).GetField($"<{nameof(ValidatorItem.ValidatorMetadata)}>k__BackingField", BindingFlags.Instance | BindingFlags.NonPublic); } public void CreateValidators(ModelValidatorProviderContext context) @@ -27,6 +31,9 @@ public void CreateValidators(ModelValidatorProviderContext context) } } + /// + /// Returns false to prevent any additional validation from being executed as a result of this provider. + /// public bool HasValidators(Type modelType, IList validatorMetadata) => false; } } From d956cc203c923496ae2be011e486eb936de5dcaf Mon Sep 17 00:00:00 2001 From: maurei Date: Wed, 30 Sep 2020 20:47:50 +0200 Subject: [PATCH 10/27] fix: comments --- .../Configuration/JsonApiModelValidationProvider.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/JsonApiDotNetCore/Configuration/JsonApiModelValidationProvider.cs b/src/JsonApiDotNetCore/Configuration/JsonApiModelValidationProvider.cs index 78ba287759..745ef57090 100644 --- a/src/JsonApiDotNetCore/Configuration/JsonApiModelValidationProvider.cs +++ b/src/JsonApiDotNetCore/Configuration/JsonApiModelValidationProvider.cs @@ -10,6 +10,7 @@ namespace JsonApiDotNetCore.Configuration /// /// This model validator provider does not create any validators, but is used to indirectly change the behavior of /// the internal through the shared object. + /// See https://github.com/json-api-dotnet/JsonApiDotNetCore/pull/847 for more info. /// internal sealed class JsonApiModelValidationProvider : IMetadataBasedModelValidatorProvider { From ce03a88a867282f5eb1451f6078813c7faffbe54 Mon Sep 17 00:00:00 2001 From: maurei Date: Wed, 30 Sep 2020 20:58:53 +0200 Subject: [PATCH 11/27] chore: rm whitespace --- src/JsonApiDotNetCore/Serialization/RequestDeserializer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/JsonApiDotNetCore/Serialization/RequestDeserializer.cs b/src/JsonApiDotNetCore/Serialization/RequestDeserializer.cs index 357b52c6d4..8accaa418e 100644 --- a/src/JsonApiDotNetCore/Serialization/RequestDeserializer.cs +++ b/src/JsonApiDotNetCore/Serialization/RequestDeserializer.cs @@ -81,7 +81,7 @@ protected override IIdentifiable SetAttributes(IIdentifiable resource, IDictiona if (attr.Property.GetCustomAttribute() != null) { bool disableValidator = attributeValues == null || !attributeValues.ContainsKey(attr.PublicName); - + if (disableValidator) { _httpContextAccessor.HttpContext.DisableRequiredValidator(attr.Property.Name, resource.GetType().Name); From 82ac21cb86b70fbd304c256cd54a8f5735841b7c Mon Sep 17 00:00:00 2001 From: maurei Date: Wed, 30 Sep 2020 21:00:22 +0200 Subject: [PATCH 12/27] fix: improve comment --- .../Configuration/JsonApiModelValidationProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/JsonApiDotNetCore/Configuration/JsonApiModelValidationProvider.cs b/src/JsonApiDotNetCore/Configuration/JsonApiModelValidationProvider.cs index 745ef57090..72af40b15c 100644 --- a/src/JsonApiDotNetCore/Configuration/JsonApiModelValidationProvider.cs +++ b/src/JsonApiDotNetCore/Configuration/JsonApiModelValidationProvider.cs @@ -33,7 +33,7 @@ public void CreateValidators(ModelValidatorProviderContext context) } /// - /// Returns false to prevent any additional validation from being executed as a result of this provider. + /// Returns false to ensure no further validation is executed through this provider. /// public bool HasValidators(Type modelType, IList validatorMetadata) => false; } From a3dbe30b24384188138be5a24a150b9ae0f8eb18 Mon Sep 17 00:00:00 2001 From: maurei Date: Wed, 30 Sep 2020 22:36:13 +0200 Subject: [PATCH 13/27] fix: build --- .../Configuration/JsonApiModelValidationProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/JsonApiDotNetCore/Configuration/JsonApiModelValidationProvider.cs b/src/JsonApiDotNetCore/Configuration/JsonApiModelValidationProvider.cs index 72af40b15c..d3c44542c9 100644 --- a/src/JsonApiDotNetCore/Configuration/JsonApiModelValidationProvider.cs +++ b/src/JsonApiDotNetCore/Configuration/JsonApiModelValidationProvider.cs @@ -9,7 +9,7 @@ namespace JsonApiDotNetCore.Configuration { /// /// This model validator provider does not create any validators, but is used to indirectly change the behavior of - /// the internal through the shared object. + /// the internal DataAnnotationsModelValidatorProvider through the shared object. /// See https://github.com/json-api-dotnet/JsonApiDotNetCore/pull/847 for more info. /// internal sealed class JsonApiModelValidationProvider : IMetadataBasedModelValidatorProvider From b26fb291b804bece7371b695ba16a2a8b7ab6956 Mon Sep 17 00:00:00 2001 From: maurei Date: Fri, 2 Oct 2020 18:39:11 +0200 Subject: [PATCH 14/27] temp --- .../ApplicationBuilderExtensions.cs | 17 +- .../Configuration/CustomProvider.cs | 178 ++++++++++++++++++ .../Configuration/CustomValidator.cs | 143 ++++++++++++++ 3 files changed, 336 insertions(+), 2 deletions(-) create mode 100644 src/JsonApiDotNetCore/Configuration/CustomProvider.cs create mode 100644 src/JsonApiDotNetCore/Configuration/CustomValidator.cs diff --git a/src/JsonApiDotNetCore/Configuration/ApplicationBuilderExtensions.cs b/src/JsonApiDotNetCore/Configuration/ApplicationBuilderExtensions.cs index eb060fa47d..291c374f84 100644 --- a/src/JsonApiDotNetCore/Configuration/ApplicationBuilderExtensions.cs +++ b/src/JsonApiDotNetCore/Configuration/ApplicationBuilderExtensions.cs @@ -1,7 +1,11 @@ using System; using JsonApiDotNetCore.Middleware; using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Mvc.DataAnnotations; +using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Localization; +using Microsoft.Extensions.Options; namespace JsonApiDotNetCore.Configuration { @@ -39,11 +43,20 @@ public static void UseJsonApi(this IApplicationBuilder builder) var routingConvention = builder.ApplicationServices.GetRequiredService(); options.Conventions.Insert(0, routingConvention); - - options.ModelValidatorProviders.Add(new JsonApiModelValidationProvider()); + + + var validationAttributeAdapterProvider = builder.ApplicationServices.GetRequiredService(); + var dataAnnotationLocalizationOptions = builder.ApplicationServices.GetRequiredService>(); + var stringLocalizerFactory = builder.ApplicationServices.GetService(); + options.ModelValidatorProviders.Add(new DataAnnotationsModelValidatorProvider_COPY(validationAttributeAdapterProvider, dataAnnotationLocalizationOptions, stringLocalizerFactory)); + // options.ModelValidatorProviders.Add(new JsonApiModelValidationProvider()); + ObjectModelValidator }; builder.UseMiddleware(); } } } + + + diff --git a/src/JsonApiDotNetCore/Configuration/CustomProvider.cs b/src/JsonApiDotNetCore/Configuration/CustomProvider.cs new file mode 100644 index 0000000000..053ad097ee --- /dev/null +++ b/src/JsonApiDotNetCore/Configuration/CustomProvider.cs @@ -0,0 +1,178 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using Microsoft.AspNetCore.Mvc.DataAnnotations; +using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; +using Microsoft.Extensions.Localization; +using Microsoft.Extensions.Options; + +namespace JsonApiDotNetCore.Configuration +{ + /// + /// An implementation of which provides validators + /// for attributes which derive from . It also provides + /// a validator for types which implement . + /// + internal sealed class DataAnnotationsModelValidatorProvider_COPY : IMetadataBasedModelValidatorProvider + { + private readonly IOptions _options; + private readonly IStringLocalizerFactory _stringLocalizerFactory; + private readonly IValidationAttributeAdapterProvider _validationAttributeAdapterProvider; + + /// + /// Create a new instance of . + /// + /// The + /// that supplies s. + /// The . + /// The . + /// and + /// are nullable only for testing ease. + public DataAnnotationsModelValidatorProvider_COPY( + IValidationAttributeAdapterProvider validationAttributeAdapterProvider, + IOptions options, + IStringLocalizerFactory stringLocalizerFactory) + { + if (validationAttributeAdapterProvider == null) + { + throw new ArgumentNullException(nameof(validationAttributeAdapterProvider)); + } + if (options == null) + { + throw new ArgumentNullException(nameof(options)); + } + + _validationAttributeAdapterProvider = validationAttributeAdapterProvider; + _options = options; + _stringLocalizerFactory = stringLocalizerFactory; + } + + public void CreateValidators(ModelValidatorProviderContext context) + { + IStringLocalizer stringLocalizer = null; + if (_stringLocalizerFactory != null && _options.Value.DataAnnotationLocalizerProvider != null) + { + stringLocalizer = _options.Value.DataAnnotationLocalizerProvider( + context.ModelMetadata.ContainerType ?? context.ModelMetadata.ModelType, + _stringLocalizerFactory); + } + + for (var i = 0; i < context.Results.Count; i++) + { + var validatorItem = context.Results[i]; + if (validatorItem.Validator != null) + { + continue; + } + + if (!(validatorItem.ValidatorMetadata is ValidationAttribute attribute)) + { + continue; + } + + var validator = new DataAnnotationsModelValidator_COPY( + _validationAttributeAdapterProvider, + attribute, + stringLocalizer); + + validatorItem.Validator = validator; + validatorItem.IsReusable = true; + // Inserts validators based on whether or not they are 'required'. We want to run + // 'required' validators first so that we get the best possible error message. + if (attribute is RequiredAttribute) + { + context.Results.Remove(validatorItem); + context.Results.Insert(0, validatorItem); + } + } + + // Produce a validator if the type supports IValidatableObject + if (typeof(IValidatableObject).IsAssignableFrom(context.ModelMetadata.ModelType)) + { + context.Results.Add(new ValidatorItem + { + Validator = new ValidatableObjectAdapter_COPY(), + IsReusable = true + }); + } + } + + public bool HasValidators(Type modelType, IList validatorMetadata) + { + if (typeof(IValidatableObject).IsAssignableFrom(modelType)) + { + return true; + } + + for (var i = 0; i < validatorMetadata.Count; i++) + { + if (validatorMetadata[i] is ValidationAttribute) + { + return true; + } + } + + return false; + } + } + + public class ValidatableObjectAdapter_COPY : IModelValidator + { + public IEnumerable Validate(ModelValidationContext context) + { + var model = context.Model; + if (model == null) + { + return Enumerable.Empty(); + } + + if (!(model is IValidatableObject validatable)) + { + var message = "null"; + + throw new InvalidOperationException(message); + } + + // The constructed ValidationContext is intentionally slightly different from what + // DataAnnotationsModelValidator creates. The instance parameter would be context.Container + // (if non-null) in that class. But, DataAnnotationsModelValidator _also_ passes context.Model + // separately to any ValidationAttribute. + var validationContext = new ValidationContext( + instance: validatable, + serviceProvider: context.ActionContext?.HttpContext?.RequestServices, + items: null) + { + DisplayName = context.ModelMetadata.GetDisplayName(), + MemberName = context.ModelMetadata.Name, + }; + + return ConvertResults(validatable.Validate(validationContext)); + } + + private IEnumerable ConvertResults(IEnumerable results) + { + foreach (var result in results) + { + if (result != ValidationResult.Success) + { + if (result.MemberNames == null || !result.MemberNames.Any()) + { + yield return new ModelValidationResult(memberName: null, message: result.ErrorMessage); + } + else + { + foreach (var memberName in result.MemberNames) + { + yield return new ModelValidationResult(memberName, result.ErrorMessage); + } + } + } + } + } + } + +} diff --git a/src/JsonApiDotNetCore/Configuration/CustomValidator.cs b/src/JsonApiDotNetCore/Configuration/CustomValidator.cs new file mode 100644 index 0000000000..840d9d72aa --- /dev/null +++ b/src/JsonApiDotNetCore/Configuration/CustomValidator.cs @@ -0,0 +1,143 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using Microsoft.AspNetCore.Mvc.DataAnnotations; +using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; +using Microsoft.Extensions.Localization; + +namespace JsonApiDotNetCore.Configuration +{ + /// + /// Validates based on the given . + /// + internal class DataAnnotationsModelValidator_COPY : IModelValidator + { + private static readonly object _emptyValidationContextInstance = new object(); + private readonly IStringLocalizer _stringLocalizer; + private readonly IValidationAttributeAdapterProvider _validationAttributeAdapterProvider; + + /// + /// Create a new instance of . + /// + /// The that defines what we're validating. + /// The used to create messages. + /// The + /// which 's will be created from. + public DataAnnotationsModelValidator_COPY( + IValidationAttributeAdapterProvider validationAttributeAdapterProvider, + ValidationAttribute attribute, + IStringLocalizer stringLocalizer) + { + if (validationAttributeAdapterProvider == null) + { + throw new ArgumentNullException(nameof(validationAttributeAdapterProvider)); + } + + if (attribute == null) + { + throw new ArgumentNullException(nameof(attribute)); + } + + _validationAttributeAdapterProvider = validationAttributeAdapterProvider; + Attribute = attribute; + _stringLocalizer = stringLocalizer; + } + + /// + /// The attribute being validated against. + /// + public ValidationAttribute Attribute { get; } + + /// + /// Validates the context against the . + /// + /// The context being validated. + /// An enumerable of the validation results. + public IEnumerable Validate(ModelValidationContext validationContext) + { + if (validationContext == null) + { + throw new ArgumentNullException(nameof(validationContext)); + } + if (validationContext.ModelMetadata == null) + { + throw new ArgumentException(); + } + if (validationContext.MetadataProvider == null) + { + throw new ArgumentException(); + } + + var metadata = validationContext.ModelMetadata; + var memberName = metadata.Name; + var container = validationContext.Container; + + var context = new ValidationContext( + instance: container ?? validationContext.Model ?? _emptyValidationContextInstance, + serviceProvider: validationContext.ActionContext?.HttpContext?.RequestServices, + items: null) + { + DisplayName = metadata.GetDisplayName(), + MemberName = memberName + }; + + var result = Attribute.GetValidationResult(validationContext.Model, context); + if (result != ValidationResult.Success) + { + string errorMessage; + if (_stringLocalizer != null && + !string.IsNullOrEmpty(Attribute.ErrorMessage) && + string.IsNullOrEmpty(Attribute.ErrorMessageResourceName) && + Attribute.ErrorMessageResourceType == null) + { + errorMessage = GetErrorMessage(validationContext) ?? result.ErrorMessage; + } + else + { + errorMessage = result.ErrorMessage; + } + + var validationResults = new List(); + if (result.MemberNames != null) + { + foreach (var resultMemberName in result.MemberNames) + { + // ModelValidationResult.MemberName is used by invoking validators (such as ModelValidator) to + // append construct the ModelKey for ModelStateDictionary. When validating at type level we + // want the returned MemberNames if specified (e.g. "person.Address.FirstName"). For property + // validation, the ModelKey can be constructed using the ModelMetadata and we should ignore + // MemberName (we don't want "person.Name.Name"). However the invoking validator does not have + // a way to distinguish between these two cases. Consequently we'll only set MemberName if this + // validation returns a MemberName that is different from the property being validated. + var newMemberName = string.Equals(resultMemberName, memberName, StringComparison.Ordinal) ? + null : + resultMemberName; + var validationResult = new ModelValidationResult(newMemberName, errorMessage); + + validationResults.Add(validationResult); + } + } + + if (validationResults.Count == 0) + { + // result.MemberNames was null or empty. + validationResults.Add(new ModelValidationResult(memberName: null, message: errorMessage)); + } + + return validationResults; + } + + return Enumerable.Empty(); + } + + private string GetErrorMessage(ModelValidationContextBase validationContext) + { + var adapter = _validationAttributeAdapterProvider.GetAttributeAdapter(Attribute, _stringLocalizer); + return adapter?.GetErrorMessage(validationContext); + } + } +} From e16bff454c009728ffbeaf49da153d868ae48145 Mon Sep 17 00:00:00 2001 From: maurei Date: Sat, 3 Oct 2020 13:25:24 +0200 Subject: [PATCH 15/27] feat: implemented --- .../Models/Article.cs | 3 + .../JsonApiDotNetCoreExample/Models/Author.cs | 2 + .../ApplicationBuilderExtensions.cs | 11 +- .../JsonApiApplicationBuilder.cs | 13 +- .../JsonApiDataAnnotationsMetadataProvider.cs | 308 +++ .../MvcInternals/JsonApiMetaDataProvider.cs | 36 + .../JsonApiModelValidationProvider.cs | 0 .../MvcInternals/JsonApiObjectValidator.cs | 113 ++ .../MvcInternals/JsonApiValidationFilter.cs | 55 + .../MvcInternals/JsonApiValidationStack.cs | 73 + .../MvcInternals/JsonApiValidationVisitor.cs | 376 ++++ .../MvcInternals/ResourceInternal.cs | 1680 +++++++++++++++++ .../Configuration/MvcInternals/Strategy.cs | 249 +++ .../Middleware/HttpContextExtensions.cs | 10 +- .../Serialization/JsonApiReader.cs | 7 +- .../Serialization/RequestDeserializer.cs | 6 +- 16 files changed, 2926 insertions(+), 16 deletions(-) create mode 100644 src/JsonApiDotNetCore/Configuration/MvcInternals/JsonApiDataAnnotationsMetadataProvider.cs create mode 100644 src/JsonApiDotNetCore/Configuration/MvcInternals/JsonApiMetaDataProvider.cs rename src/JsonApiDotNetCore/Configuration/{ => MvcInternals}/JsonApiModelValidationProvider.cs (100%) create mode 100644 src/JsonApiDotNetCore/Configuration/MvcInternals/JsonApiObjectValidator.cs create mode 100644 src/JsonApiDotNetCore/Configuration/MvcInternals/JsonApiValidationFilter.cs create mode 100644 src/JsonApiDotNetCore/Configuration/MvcInternals/JsonApiValidationStack.cs create mode 100644 src/JsonApiDotNetCore/Configuration/MvcInternals/JsonApiValidationVisitor.cs create mode 100644 src/JsonApiDotNetCore/Configuration/MvcInternals/ResourceInternal.cs create mode 100644 src/JsonApiDotNetCore/Configuration/MvcInternals/Strategy.cs diff --git a/src/Examples/JsonApiDotNetCoreExample/Models/Article.cs b/src/Examples/JsonApiDotNetCoreExample/Models/Article.cs index 455ed51039..357241b54d 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Models/Article.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Models/Article.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using JsonApiDotNetCore.Resources; using JsonApiDotNetCore.Resources.Annotations; @@ -8,9 +9,11 @@ namespace JsonApiDotNetCoreExample.Models public sealed class Article : Identifiable { [Attr] + [Required] public string Caption { get; set; } [Attr] + [Required] public string Url { get; set; } [HasOne] diff --git a/src/Examples/JsonApiDotNetCoreExample/Models/Author.cs b/src/Examples/JsonApiDotNetCoreExample/Models/Author.cs index 7280263468..18b8934a76 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Models/Author.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Models/Author.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; using JsonApiDotNetCore.Resources; using JsonApiDotNetCore.Resources.Annotations; @@ -8,6 +9,7 @@ namespace JsonApiDotNetCoreExample.Models public sealed class Author : Identifiable { [Attr] + [Required] public string FirstName { get; set; } [Attr] diff --git a/src/JsonApiDotNetCore/Configuration/ApplicationBuilderExtensions.cs b/src/JsonApiDotNetCore/Configuration/ApplicationBuilderExtensions.cs index 291c374f84..2cbb65d9f0 100644 --- a/src/JsonApiDotNetCore/Configuration/ApplicationBuilderExtensions.cs +++ b/src/JsonApiDotNetCore/Configuration/ApplicationBuilderExtensions.cs @@ -45,12 +45,13 @@ public static void UseJsonApi(this IApplicationBuilder builder) options.Conventions.Insert(0, routingConvention); - var validationAttributeAdapterProvider = builder.ApplicationServices.GetRequiredService(); - var dataAnnotationLocalizationOptions = builder.ApplicationServices.GetRequiredService>(); - var stringLocalizerFactory = builder.ApplicationServices.GetService(); - options.ModelValidatorProviders.Add(new DataAnnotationsModelValidatorProvider_COPY(validationAttributeAdapterProvider, dataAnnotationLocalizationOptions, stringLocalizerFactory)); + // var validationAttributeAdapterProvider = builder.ApplicationServices.GetRequiredService(); + // var dataAnnotationLocalizationOptions = builder.ApplicationServices.GetRequiredService>(); + // var stringLocalizerFactory = builder.ApplicationServices.GetService(); + // options.ModelValidatorProviders.Add(new DataAnnotationsModelValidatorProvider_COPY(validationAttributeAdapterProvider, dataAnnotationLocalizationOptions, stringLocalizerFactory)); // options.ModelValidatorProviders.Add(new JsonApiModelValidationProvider()); - ObjectModelValidator + // options.ModelMetadataDetailsProviders.Add(new JsonApiDataAnnotationsMetadataProvider()); + }; builder.UseMiddleware(); diff --git a/src/JsonApiDotNetCore/Configuration/JsonApiApplicationBuilder.cs b/src/JsonApiDotNetCore/Configuration/JsonApiApplicationBuilder.cs index 4ffc42205a..e628e63e67 100644 --- a/src/JsonApiDotNetCore/Configuration/JsonApiApplicationBuilder.cs +++ b/src/JsonApiDotNetCore/Configuration/JsonApiApplicationBuilder.cs @@ -17,10 +17,13 @@ using JsonApiDotNetCore.Services; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.ModelBinding; +using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; namespace JsonApiDotNetCore.Configuration { @@ -129,7 +132,15 @@ public void ConfigureServiceContainer(ICollection dbContextTypes) _services.AddScoped(typeof(IDbContextResolver), contextResolverType); } } - + + _services.AddSingleton(); + _services.AddSingleton(s => + { + var options = s.GetRequiredService>().Value; + var metadataProvider = s.GetRequiredService(); + return new JsonApiObjectValidator(metadataProvider, options.ModelValidatorProviders, options); + }); + AddResourceLayer(); AddRepositoryLayer(); AddServiceLayer(); diff --git a/src/JsonApiDotNetCore/Configuration/MvcInternals/JsonApiDataAnnotationsMetadataProvider.cs b/src/JsonApiDotNetCore/Configuration/MvcInternals/JsonApiDataAnnotationsMetadataProvider.cs new file mode 100644 index 0000000000..dd4bab001c --- /dev/null +++ b/src/JsonApiDotNetCore/Configuration/MvcInternals/JsonApiDataAnnotationsMetadataProvider.cs @@ -0,0 +1,308 @@ +// // Copyright (c) .NET Foundation. All rights reserved. +// // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// +// using System; +// using System.Collections; +// using System.Collections.Generic; +// using System.ComponentModel; +// using System.ComponentModel.DataAnnotations; +// using System.Diagnostics.Contracts; +// using System.Linq; +// using System.Reflection; +// using System.Runtime.InteropServices; +// using System.Runtime.InteropServices.ComTypes; +// using Microsoft.AspNetCore.Mvc; +// using Microsoft.AspNetCore.Mvc.DataAnnotations; +// using Microsoft.AspNetCore.Mvc.ModelBinding; +// using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata; +// using Microsoft.Extensions.Localization; +// using Microsoft.Extensions.Options; +// +// namespace JsonApiDotNetCore.Configuration +// { +// /// +// /// An implementation of and for +// /// the System.ComponentModel.DataAnnotations attribute classes. +// /// +// internal class JsonApiDataAnnotationsMetadataProvider : IValidationMetadataProvider +// { +// // The [Nullable] attribute is synthesized by the compiler. It's best to just compare the type name. +// private const string NullableAttributeFullTypeName = "System.Runtime.CompilerServices.NullableAttribute"; +// private const string NullableFlagsFieldName = "NullableFlags"; +// private DataAnnotationsMetadataProvider x +// private const string NullableContextAttributeFullName = "System.Runtime.CompilerServices.NullableContextAttribute"; +// private const string NullableContextFlagsFieldName = "Flag"; +// +// private readonly IStringLocalizerFactory _stringLocalizerFactory; +// private readonly MvcOptions _options; +// private readonly MvcDataAnnotationsLocalizationOptions _localizationOptions; +// +// public JsonApiDataAnnotationsMetadataProvider( +// MvcOptions options, +// IOptions localizationOptions, +// IStringLocalizerFactory stringLocalizerFactory) +// { +// if (options == null) +// { +// throw new ArgumentNullException(nameof(options)); +// } +// +// if (localizationOptions == null) +// { +// throw new ArgumentNullException(nameof(localizationOptions)); +// } +// +// _options = options; +// _localizationOptions = localizationOptions.Value; +// _stringLocalizerFactory = stringLocalizerFactory; +// } +// +// +// /// +// public void CreateValidationMetadata(ValidationMetadataProviderContext context) +// { +// if (context == null) +// { +// throw new ArgumentNullException(nameof(context)); +// } +// +// // Read interface .Count once rather than per iteration +// var contextAttributes = context.Attributes; +// var contextAttributesCount = contextAttributes.Count; +// var attributes = new List(contextAttributesCount); +// +// for (var i = 0; i < contextAttributesCount; i++) +// { +// var attribute = contextAttributes[i]; +// if (attribute is ValidationProviderAttribute validationProviderAttribute) +// { +// attributes.AddRange(validationProviderAttribute.GetValidationAttributes()); +// } +// else +// { +// attributes.Add(attribute); +// } +// } +// +// // RequiredAttribute marks a property as required by validation - this means that it +// // must have a non-null value on the model during validation. +// var requiredAttribute = attributes.OfType().FirstOrDefault(); +// +// // For non-nullable reference types, treat them as-if they had an implicit [Required]. +// // This allows the developer to specify [Required] to customize the error message, so +// // if they already have [Required] then there's no need for us to do this check. +// if (!_options.SuppressImplicitRequiredAttributeForNonNullableReferenceTypes && +// requiredAttribute == null && +// !context.Key.ModelType.IsValueType && +// context.Key.MetadataKind != ModelMetadataKind.Type) +// { +// var addInferredRequiredAttribute = false; +// if (context.Key.MetadataKind == ModelMetadataKind.Type) +// { +// // Do nothing. +// } +// else if (context.Key.MetadataKind == ModelMetadataKind.Property) +// { +// var property = context.Key.PropertyInfo; +// if (property is null) +// { +// // PropertyInfo was unavailable on ModelIdentity prior to 3.1. +// // Making a cogent argument about the nullability of the property requires inspecting the declared type, +// // since looking at the runtime type may result in false positives: https://github.com/aspnet/AspNetCore/issues/14812 +// // The only way we could arrive here is if the ModelMetadata was constructed using the non-default provider. +// // We'll cursorily examine the attributes on the property, but not the ContainerType to make a decision about it's nullability. +// +// if (HasNullableAttribute(context.PropertyAttributes, out var propertyHasNullableAttribute)) +// { +// addInferredRequiredAttribute = propertyHasNullableAttribute; +// } +// } +// else +// { +// addInferredRequiredAttribute = IsNullableReferenceType( +// property.DeclaringType, +// member: null, +// context.PropertyAttributes); +// } +// } +// else if (context.Key.MetadataKind == ModelMetadataKind.Parameter) +// { +// addInferredRequiredAttribute = IsNullableReferenceType( +// context.Key.ParameterInfo?.Member.ReflectedType, +// context.Key.ParameterInfo.Member, +// context.ParameterAttributes); +// } +// else +// { +// throw new InvalidOperationException("Unsupported ModelMetadataKind: " + context.Key.MetadataKind); +// } +// +// if (addInferredRequiredAttribute) +// { +// // Since this behavior specifically relates to non-null-ness, we will use the non-default +// // option to tolerate empty/whitespace strings. empty/whitespace INPUT will still result in +// // a validation error by default because we convert empty/whitespace strings to null +// // unless you say otherwise. +// requiredAttribute = new RequiredAttribute() +// { +// AllowEmptyStrings = true, +// }; +// attributes.Add(requiredAttribute); +// } +// } +// +// if (requiredAttribute != null) +// { +// context.ValidationMetadata.IsRequired = true; +// context.ValidationMetadata.PropertyValidationFilter = new JsonApiPartialPatchFilter(); +// } +// +// foreach (var attribute in attributes.OfType()) +// { +// // If another provider has already added this attribute, do not repeat it. +// // This will prevent attributes like RemoteAttribute (which implement ValidationAttribute and +// // IClientModelValidator) to be added to the ValidationMetadata twice. +// // This is to ensure we do not end up with duplication validation rules on the client side. +// if (!context.ValidationMetadata.ValidatorMetadata.Contains(attribute)) +// { +// context.ValidationMetadata.ValidatorMetadata.Add(attribute); +// } +// } +// } +// +// private static string GetDisplayName(FieldInfo field, IStringLocalizer stringLocalizer) +// { +// var display = field.GetCustomAttribute(inherit: false); +// if (display != null) +// { +// // Note [Display(Name = "")] is allowed but we will not attempt to localize the empty name. +// var name = display.GetName(); +// if (stringLocalizer != null && !string.IsNullOrEmpty(name) && display.ResourceType == null) +// { +// name = stringLocalizer[name]; +// } +// +// return name ?? field.Name; +// } +// +// return field.Name; +// } +// +// // Return non-empty group specified in a [Display] attribute for a field, if any; string.Empty otherwise. +// private static string GetDisplayGroup(FieldInfo field) +// { +// var display = field.GetCustomAttribute(inherit: false); +// if (display != null) +// { +// // Note [Display(Group = "")] is allowed. +// var group = display.GetGroupName(); +// if (group != null) +// { +// return group; +// } +// } +// +// return string.Empty; +// } +// +// internal static bool IsNullableReferenceType(Type containingType, MemberInfo member, IEnumerable attributes) +// { +// if (HasNullableAttribute(attributes, out var result)) +// { +// return result; +// } +// +// return IsNullableBasedOnContext(containingType, member); +// } +// +// // Internal for testing +// internal static bool HasNullableAttribute(IEnumerable attributes, out bool isNullable) +// { +// // [Nullable] is compiler synthesized, comparing by name. +// var nullableAttribute = attributes +// .FirstOrDefault(a => string.Equals(a.GetType().FullName, NullableAttributeFullTypeName, StringComparison.Ordinal)); +// if (nullableAttribute == null) +// { +// isNullable = false; +// return false; // [Nullable] not found +// } +// +// // We don't handle cases where generics and NNRT are used. This runs into a +// // fundamental limitation of ModelMetadata - we use a single Type and Property/Parameter +// // to look up the metadata. However when generics are involved and NNRT is in use +// // the distance between the [Nullable] and member we're looking at is potentially +// // unbounded. +// // +// // See: https://github.com/dotnet/roslyn/blob/master/docs/features/nullable-reference-types.md#annotations +// if (nullableAttribute.GetType().GetField(NullableFlagsFieldName) is FieldInfo field && +// field.GetValue(nullableAttribute) is byte[] flags && +// flags.Length >= 0 && +// flags[0] == 1) // First element is the property/parameter type. +// { +// isNullable = true; +// return true; // [Nullable] found and type is an NNRT +// } +// +// isNullable = false; +// return true; // [Nullable] found but type is not an NNRT +// } +// +// internal static bool IsNullableBasedOnContext(Type containingType, MemberInfo member) +// { +// // For generic types, inspecting the nullability requirement additionally requires +// // inspecting the nullability constraint on generic type parameters. This is fairly non-triviial +// // so we'll just avoid calculating it. Users should still be able to apply an explicit [Required] +// // attribute on these members. +// if (containingType.IsGenericType) +// { +// return false; +// } +// +// // The [Nullable] and [NullableContext] attributes are not inherited. +// // +// // The [NullableContext] attribute can appear on a method or on the module. +// var attributes = member?.GetCustomAttributes(inherit: false) ?? Array.Empty(); +// var isNullable = AttributesHasNullableContext(attributes); +// if (isNullable != null) +// { +// return isNullable.Value; +// } +// +// // Check on the containing type +// var type = containingType; +// do +// { +// attributes = type.GetCustomAttributes(inherit: false); +// isNullable = AttributesHasNullableContext(attributes); +// if (isNullable != null) +// { +// return isNullable.Value; +// } +// +// type = type.DeclaringType; +// } +// while (type != null); +// +// // If we don't find the attribute on the declaring type then repeat at the module level +// attributes = containingType.Module.GetCustomAttributes(inherit: false); +// isNullable = AttributesHasNullableContext(attributes); +// return isNullable ?? false; +// +// bool? AttributesHasNullableContext(object[] attributes) +// { +// var nullableContextAttribute = attributes +// .FirstOrDefault(a => string.Equals(a.GetType().FullName, NullableContextAttributeFullName, StringComparison.Ordinal)); +// if (nullableContextAttribute != null) +// { +// if (nullableContextAttribute.GetType().GetField(NullableContextFlagsFieldName) is FieldInfo field && +// field.GetValue(nullableContextAttribute) is byte @byte) +// { +// return @byte == 1; // [NullableContext] found +// } +// } +// +// return null; +// } +// } +// } +// } diff --git a/src/JsonApiDotNetCore/Configuration/MvcInternals/JsonApiMetaDataProvider.cs b/src/JsonApiDotNetCore/Configuration/MvcInternals/JsonApiMetaDataProvider.cs new file mode 100644 index 0000000000..10a0aee3c0 --- /dev/null +++ b/src/JsonApiDotNetCore/Configuration/MvcInternals/JsonApiMetaDataProvider.cs @@ -0,0 +1,36 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.ModelBinding; +using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata; +using Microsoft.Extensions.Options; + +namespace JsonApiDotNetCore.Configuration +{ + /// + /// A default implementation of based on reflection. + /// + public class JsonApiModelMetadataProvider : DefaultModelMetadataProvider + { + public JsonApiModelMetadataProvider(ICompositeMetadataDetailsProvider detailsProvider) + : base(detailsProvider) { } + + public JsonApiModelMetadataProvider(ICompositeMetadataDetailsProvider detailsProvider, IOptions optionsAccessor) + : base(detailsProvider, optionsAccessor) { } + + protected override ModelMetadata CreateModelMetadata(DefaultMetadataDetails entry) + { + var metadata = new DefaultModelMetadata(this, DetailsProvider, entry, ModelBindingMessageProvider) ; + + var isRequired = metadata.ValidationMetadata.IsRequired; + + if (isRequired != null && isRequired.Value) + { + metadata.ValidationMetadata.PropertyValidationFilter = new PartialPatchValidationFilter(); + } + + return metadata; + } + } +} diff --git a/src/JsonApiDotNetCore/Configuration/JsonApiModelValidationProvider.cs b/src/JsonApiDotNetCore/Configuration/MvcInternals/JsonApiModelValidationProvider.cs similarity index 100% rename from src/JsonApiDotNetCore/Configuration/JsonApiModelValidationProvider.cs rename to src/JsonApiDotNetCore/Configuration/MvcInternals/JsonApiModelValidationProvider.cs diff --git a/src/JsonApiDotNetCore/Configuration/MvcInternals/JsonApiObjectValidator.cs b/src/JsonApiDotNetCore/Configuration/MvcInternals/JsonApiObjectValidator.cs new file mode 100644 index 0000000000..92596c1073 --- /dev/null +++ b/src/JsonApiDotNetCore/Configuration/MvcInternals/JsonApiObjectValidator.cs @@ -0,0 +1,113 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.ModelBinding; +using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; + +namespace JsonApiDotNetCore.Configuration +{ + /// + /// The default implementation of . + /// + internal class JsonApiObjectValidator : ObjectModelValidator + { + private readonly IModelMetadataProvider _modelMetadataProvider; + private readonly MvcOptions _mvcOptions; + private readonly ValidatorCache _validatorCache; + private readonly CompositeModelValidatorProvider _validatorProvider; + + /// + /// Initializes a new instance of . + /// + /// The . + /// The list of . + /// Accessor to . + public JsonApiObjectValidator( + IModelMetadataProvider modelMetadataProvider, + IList validatorProviders, + MvcOptions mvcOptions) + : base(modelMetadataProvider, validatorProviders) + { + if (validatorProviders == null) + { + throw new ArgumentNullException(nameof(validatorProviders)); + } + + _modelMetadataProvider = modelMetadataProvider ?? throw new ArgumentNullException(nameof(modelMetadataProvider)); + _validatorCache = new ValidatorCache(); + _validatorProvider = new CompositeModelValidatorProvider(validatorProviders); + _mvcOptions = mvcOptions; + } + + public override ValidationVisitor GetValidationVisitor( + ActionContext actionContext, + IModelValidatorProvider validatorProvider, + ValidatorCache validatorCache, + IModelMetadataProvider metadataProvider, + ValidationStateDictionary validationState) + { + var visitor = new JsonApiValidationVisitor( + actionContext, + validatorProvider, + validatorCache, + metadataProvider, + validationState) + { + MaxValidationDepth = _mvcOptions.MaxValidationDepth, + ValidateComplexTypesIfChildValidationFails = _mvcOptions.ValidateComplexTypesIfChildValidationFails, + }; + + return visitor; + } + + /// + public override void Validate( + ActionContext actionContext, + ValidationStateDictionary validationState, + string prefix, + object model) + { + var visitor = GetValidationVisitor( + actionContext, + _validatorProvider, + _validatorCache, + _modelMetadataProvider, + validationState); + + var metadata = model == null ? null : _modelMetadataProvider.GetMetadataForType(model.GetType()); + visitor.Validate(metadata, prefix, model, alwaysValidateAtTopLevel: false); + } + + /// + /// Validates the provided object model. + /// If is and the 's + /// is , will add one or more + /// model state errors that + /// would not. + /// + /// The . + /// The . + /// The model prefix key. + /// The model object. + /// The . + public override void Validate( + ActionContext actionContext, + ValidationStateDictionary validationState, + string prefix, + object model, + ModelMetadata metadata) + { + var visitor = GetValidationVisitor( + actionContext, + _validatorProvider, + _validatorCache, + _modelMetadataProvider, + validationState); + + visitor.Validate(metadata, prefix, model, alwaysValidateAtTopLevel: metadata.IsRequired); + } + } +} diff --git a/src/JsonApiDotNetCore/Configuration/MvcInternals/JsonApiValidationFilter.cs b/src/JsonApiDotNetCore/Configuration/MvcInternals/JsonApiValidationFilter.cs new file mode 100644 index 0000000000..fd90db90da --- /dev/null +++ b/src/JsonApiDotNetCore/Configuration/MvcInternals/JsonApiValidationFilter.cs @@ -0,0 +1,55 @@ +using System; +using JsonApiDotNetCore.Middleware; +using JsonApiDotNetCore.Resources; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; +using Microsoft.Extensions.DependencyInjection; + +namespace JsonApiDotNetCore.Configuration +{ + internal sealed class PartialPatchValidationFilter : IPropertyValidationFilter + { + /// + public bool ShouldValidateEntry(ValidationEntry entry, ValidationEntry parentEntry) => true; + + public bool ShouldValidateEntry(ValidationEntry entry, ValidationEntry parentEntry, + IServiceProvider serviceProvider) + { + if (serviceProvider == null) throw new ArgumentException(nameof(serviceProvider)); + + var request = serviceProvider.GetRequiredService(); + var httpContextAccessor = serviceProvider.GetRequiredService(); + var targetedFields = serviceProvider.GetRequiredService(); + + if (request.Kind == EndpointKind.Primary && string.IsNullOrEmpty(parentEntry.Key) && RequiredFieldIsTargeted(entry, targetedFields, httpContextAccessor)) + { + return true; + } + + return false; + } + + private bool RequiredFieldIsTargeted(ValidationEntry entry, ITargetedFields targetedFields, IHttpContextAccessor httpContextAccessor) + { + var requestMethod = httpContextAccessor.HttpContext.Request.Method; + + if (requestMethod == HttpMethods.Post) + { + return true; + } + + if (requestMethod == HttpMethods.Patch) + { + foreach (var attribute in targetedFields.Attributes) + { + if (attribute.Property.Name == entry.Key) + { + return true; + } + } + } + + return false; + } + } +} diff --git a/src/JsonApiDotNetCore/Configuration/MvcInternals/JsonApiValidationStack.cs b/src/JsonApiDotNetCore/Configuration/MvcInternals/JsonApiValidationStack.cs new file mode 100644 index 0000000000..40f054a7af --- /dev/null +++ b/src/JsonApiDotNetCore/Configuration/MvcInternals/JsonApiValidationStack.cs @@ -0,0 +1,73 @@ +// // Copyright (c) .NET Foundation. All rights reserved. +// // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// +// using System.Collections.Generic; +// using System.Diagnostics; +// using Microsoft.EntityFrameworkCore.Internal; +// +// namespace JsonApiDotNetCore.Configuration +// { +// internal class ValidationStack +// { +// public int Count => HashSet?.Count ?? List.Count; +// +// // We tested the performance of a list at size 15 and found it still better than hashset, but to avoid a costly +// // O(n) search at larger n we set the cutoff to 20. If someone finds the point where they intersect feel free to change this number. +// internal const int CutOff = 20; +// +// internal List List { get; } = new List(); +// +// internal HashSet HashSet { get; set; } +// +// public bool Push(object model) +// { +// if (HashSet != null) +// { +// return HashSet.Add(model); +// } +// +// if (ListContains(model)) +// { +// return false; +// } +// +// List.Add(model); +// +// if (HashSet == null && List.Count > CutOff) +// { +// HashSet = new HashSet(List, ReferenceEqualityComparer.Instance); +// } +// +// return true; +// } +// +// public void Pop(object model) +// { +// if (HashSet != null) +// { +// HashSet.Remove(model); +// } +// else +// { +// if (model != null) +// { +// Debug.Assert(ReferenceEquals(List[List.Count - 1], model)); +// List.RemoveAt(List.Count - 1); +// } +// } +// } +// +// private bool ListContains(object model) +// { +// for (var i = 0; i < List.Count; i++) +// { +// if (ReferenceEquals(model, List[i])) +// { +// return true; +// } +// } +// +// return false; +// } +// } +// } diff --git a/src/JsonApiDotNetCore/Configuration/MvcInternals/JsonApiValidationVisitor.cs b/src/JsonApiDotNetCore/Configuration/MvcInternals/JsonApiValidationVisitor.cs new file mode 100644 index 0000000000..bb9a665dbf --- /dev/null +++ b/src/JsonApiDotNetCore/Configuration/MvcInternals/JsonApiValidationVisitor.cs @@ -0,0 +1,376 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Runtime.CompilerServices; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Core; +using Microsoft.AspNetCore.Mvc.ModelBinding; +using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata; +using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; + +namespace JsonApiDotNetCore.Configuration +{ + internal sealed class JsonApiValidationVisitor : ValidationVisitor + { + // private readonly ValidationStack _currentPath; + + public JsonApiValidationVisitor( + ActionContext actionContext, + IModelValidatorProvider validatorProvider, + ValidatorCache validatorCache, + IModelMetadataProvider metadataProvider, + ValidationStateDictionary validationState) + : base(actionContext, validatorProvider, validatorCache, metadataProvider, validationState) { } + // { + // if (actionContext == null) + // { + // throw new ArgumentNullException(nameof(actionContext)); + // } + // + // if (validatorProvider == null) + // { + // throw new ArgumentNullException(nameof(validatorProvider)); + // } + // + // if (validatorCache == null) + // { + // throw new ArgumentNullException(nameof(validatorCache)); + // } + // + // + // _currentPath = new ValidationStack(); + // } + + + protected override bool VisitChildren(IValidationStrategy strategy) + { + var isValid = true; + var enumerator = strategy.GetChildren(Metadata, Key, Model); + var parentEntry = new ValidationEntry(Metadata, Key, Model); + + while (enumerator.MoveNext()) + { + var entry = enumerator.Current; + var metadata = entry.Metadata; + var key = entry.Key; + + var jsonApiFilter = metadata.PropertyValidationFilter as PartialPatchValidationFilter; + var serviceProvider = Context?.HttpContext?.RequestServices; + + if (metadata.PropertyValidationFilter?.ShouldValidateEntry(entry, parentEntry) == false + || jsonApiFilter != null && jsonApiFilter.ShouldValidateEntry(entry, parentEntry, serviceProvider) == false ) + { + SuppressValidation(key); + continue; + } + + isValid &= Visit(metadata, key, entry.Model); + } + + return isValid; + } + + + // /// + // /// Validates a object. + // /// + // /// The associated with the model. + // /// The model prefix key. + // /// The model object. + // /// If true, applies validation rules even if the top-level value is null. + // /// true if the object is valid, otherwise false. + // public override bool Validate(ModelMetadata metadata, string key, object model, bool alwaysValidateAtTopLevel) + // { + // + // if (model == null && key != null && !alwaysValidateAtTopLevel) + // { + // var entry = ModelState[key]; + // + // // Rationale: We might see the same model state key for two different objects and want to preserve any + // // known invalidity. + // if (entry != null && entry.ValidationState != ModelValidationState.Invalid) + // { + // entry.ValidationState = ModelValidationState.Valid; + // } + // + // return true; + // } + // + // return Visit(metadata, key, model); + // } + + // /// + // /// Validates a single node in a model object graph. + // /// + // /// true if the node is valid, otherwise false. + // protected override bool ValidateNode() + // { + // var state = ModelState.GetValidationState(Key); + // + // // Rationale: we might see the same model state key used for two different objects. + // // We want to run validation unless it's already known that this key is invalid. + // if (state != ModelValidationState.Invalid) + // { + // var validators = Cache.GetValidators(Metadata, ValidatorProvider); + // + // var count = validators.Count; + // if (count > 0) + // { + // var context = new ModelValidationContext( + // Context, + // Metadata, + // MetadataProvider, + // Container, + // Model); + // + // var results = new List(); + // for (var i = 0; i < count; i++) + // { + // results.AddRange(validators[i].Validate(context)); + // } + // + // var resultsCount = results.Count; + // for (var i = 0; i < resultsCount; i++) + // { + // var result = results[i]; + // var key = ModelNames.CreatePropertyModelName(Key, result.MemberName); + // + // // It's OK for key to be the empty string here. This can happen when a top + // // level object implements IValidatableObject. + // ModelState.TryAddModelError(key, result.Message); + // } + // } + // } + // + // state = ModelState.GetFieldValidationState(Key); + // if (state == ModelValidationState.Invalid) + // { + // return false; + // } + // else + // { + // // If the field has an entry in ModelState, then record it as valid. Don't create + // // extra entries if they don't exist already. + // var entry = ModelState[Key]; + // if (entry != null) + // { + // entry.ValidationState = ModelValidationState.Valid; + // } + // + // return true; + // } + // } + // + // protected override bool Visit(ModelMetadata metadata, string key, object model) + // { + // RuntimeHelpers.EnsureSufficientExecutionStack(); + // + // if (model != null && !_currentPath.Push(model)) + // { + // // This is a cycle, bail. + // return true; + // } + // + // if (MaxValidationDepth != null && _currentPath.Count > MaxValidationDepth) + // { + // // Non cyclic but too deep an object graph. + // + // // Pop the current model to make ValidationStack.Dispose happy + // _currentPath.Pop(model); + // + // string message; + // switch (metadata.MetadataKind) + // { + // case ModelMetadataKind.Property: + // message = ResourcesInternal.FormatValidationVisitor_ExceededMaxPropertyDepth(nameof(JsonApiValidationVisitor), MaxValidationDepth, metadata.Name, metadata.ContainerType); + // break; + // + // default: + // // Since the minimum depth is never 0, MetadataKind can never be Parameter. Consequently we only special case MetadataKind.Property. + // message = ResourcesInternal.FormatValidationVisitor_ExceededMaxDepth(nameof(JsonApiValidationVisitor), MaxValidationDepth, metadata.ModelType); + // break; + // } + // + // message += " " + ResourcesInternal.FormatValidationVisitor_ExceededMaxDepthFix(nameof(MvcOptions), nameof(MvcOptions.MaxValidationDepth)); + // throw new InvalidOperationException(message) + // { + // HelpLink = "https://aka.ms/AA21ue1", + // }; + // } + // + // var entry = GetValidationEntry(model); + // key = entry?.Key ?? key ?? string.Empty; + // metadata = entry?.Metadata ?? metadata; + // var strategy = entry?.Strategy; + // + // if (ModelState.HasReachedMaxErrors) + // { + // SuppressValidation(key); + // return false; + // } + // else if (entry != null && entry.SuppressValidation) + // { + // // Use the key on the entry, because we might not have entries in model state. + // SuppressValidation(entry.Key); + // _currentPath.Pop(model); + // return true; + // } + // // If the metadata indicates that no validators exist AND the aggregate state for the key says that the model graph + // // is not invalid (i.e. is one of Unvalidated, Valid, or Skipped) we can safely mark the graph as valid. + // else if (metadata.HasValidators == false && + // ModelState.GetFieldValidationState(key) != ModelValidationState.Invalid) + // { + // // No validators will be created for this graph of objects. Mark it as valid if it wasn't previously validated. + // var entries = ModelState.FindKeysWithPrefix(key); + // foreach (var item in entries) + // { + // if (item.Value.ValidationState == ModelValidationState.Unvalidated) + // { + // item.Value.ValidationState = ModelValidationState.Valid; + // } + // } + // + // _currentPath.Pop(model); + // return true; + // } + // + // using (JsonApiStateManager.Recurse(this, key ?? string.Empty, metadata, model, strategy)) + // { + // if (Metadata.IsEnumerableType) + // { + // return VisitComplexType(JsonApiDefaultCollectionValidationStrategy.Instance); + // } + // + // if (Metadata.IsComplexType) + // { + // return VisitComplexType(JsonApiDefaultComplexObjectValidationStrategy.Instance); + // } + // + // return VisitSimpleType(); + // } + // } + // + // // Covers everything VisitSimpleType does not i.e. both enumerations and complex types. + // protected override bool VisitComplexType(IValidationStrategy defaultStrategy) + // { + // var isValid = true; + // if (Model != null && Metadata.ValidateChildren) + // { + // var strategy = Strategy ?? defaultStrategy; + // isValid = VisitChildren(strategy); + // } + // else if (Model != null) + // { + // // Suppress validation for the entries matching this prefix. This will temporarily set + // // the current node to 'skipped' but we're going to visit it right away, so subsequent + // // code will set it to 'valid' or 'invalid' + // SuppressValidation(Key); + // } + // + // // Double-checking HasReachedMaxErrors just in case this model has no properties. + // // If validation has failed for any children, only validate the parent if ValidateComplexTypesIfChildValidationFails is true. + // if ((isValid || ValidateComplexTypesIfChildValidationFails) && !ModelState.HasReachedMaxErrors) + // { + // isValid &= ValidateNode(); + // } + // + // return isValid; + // } + // + // protected override bool VisitSimpleType() + // { + // if (ModelState.HasReachedMaxErrors) + // { + // SuppressValidation(Key); + // return false; + // } + // + // return ValidateNode(); + // } + // + // protected override void SuppressValidation(string key) + // { + // if (key == null) + // { + // // If the key is null, that means that we shouldn't expect any entries in ModelState for + // // this value, so there's nothing to do. + // return; + // } + // + // var entries = ModelState.FindKeysWithPrefix(key); + // foreach (var entry in entries) + // { + // if (entry.Value.ValidationState != ModelValidationState.Invalid) + // { + // entry.Value.ValidationState = ModelValidationState.Skipped; + // } + // } + // } + // + // protected override ValidationStateEntry GetValidationEntry(object model) + // { + // if (model == null || ValidationState == null) + // { + // return null; + // } + // + // ValidationState.TryGetValue(model, out var entry); + // return entry; + // } + // + // protected readonly struct JsonApiStateManager : IDisposable + // { + // private readonly JsonApiValidationVisitor _visitor; + // private readonly object _container; + // private readonly string _key; + // private readonly ModelMetadata _metadata; + // private readonly object _model; + // private readonly object _newModel; + // private readonly IValidationStrategy _strategy; + // + // public static JsonApiStateManager Recurse( + // JsonApiValidationVisitor visitor, + // string key, + // ModelMetadata metadata, + // object model, + // IValidationStrategy strategy) + // { + // var recursifier = new JsonApiStateManager(visitor, model); + // + // visitor.Container = visitor.Model; + // visitor.Key = key; + // visitor.Metadata = metadata; + // visitor.Model = model; + // visitor.Strategy = strategy; + // + // return recursifier; + // } + // + // public JsonApiStateManager(JsonApiValidationVisitor visitor, object newModel) + // { + // _visitor = visitor; + // _newModel = newModel; + // + // _container = _visitor.Container; + // _key = _visitor.Key; + // _metadata = _visitor.Metadata; + // _model = _visitor.Model; + // _strategy = _visitor.Strategy; + // } + // + // public void Dispose() + // { + // _visitor.Container = _container; + // _visitor.Key = _key; + // _visitor.Metadata = _metadata; + // _visitor.Model = _model; + // _visitor.Strategy = _strategy; + // + // _visitor._currentPath.Pop(_newModel); + // } + // } + } +} diff --git a/src/JsonApiDotNetCore/Configuration/MvcInternals/ResourceInternal.cs b/src/JsonApiDotNetCore/Configuration/MvcInternals/ResourceInternal.cs new file mode 100644 index 0000000000..9ae9244524 --- /dev/null +++ b/src/JsonApiDotNetCore/Configuration/MvcInternals/ResourceInternal.cs @@ -0,0 +1,1680 @@ +// // Decompiled with JetBrains decompiler +// // Type: ResourcesInternal +// // Assembly: Microsoft.AspNetCore.Mvc.Core, Version=3.1.5.0, Culture=neutral, PublicKeyToken=adb9793829ddae60 +// // MVID: C072ADA3-420F-4E08-8493-043C3240EB59 +// // Assembly location: /usr/local/share/dotnet/shared/Microsoft.AspNetCore.App/3.1.5/Microsoft.AspNetCore.Mvc.Core.dll +// +// using System; +// using System.Globalization; +// using System.Resources; +// using System.Runtime.CompilerServices; +// +// namespace JsonApiDotNetCore.Configuration +// { +// internal static class ResourcesInternal +// { +// private static ResourceManager s_resourceManager; +// +// internal static ResourceManager ResourceManager +// { +// get +// { +// return s_resourceManager ?? (s_resourceManager = new ResourceManager(typeof (ResourcesInternal))); +// } +// } +// +// internal static CultureInfo Culture { get; set; } +// +// [MethodImpl(MethodImplOptions.AggressiveInlining)] +// internal static string GetResourceString(string resourceKey, string defaultValue = null) +// { +// return ResourcesInternal.ResourceManager.GetString(resourceKey, ResourcesInternal.Culture); +// } +// +// private static string GetResourceString(string resourceKey, string[] formatterNames) +// { +// string str = ResourcesInternal.GetResourceString(resourceKey, (string) null); +// if (formatterNames != null) +// { +// for (int index = 0; index < formatterNames.Length; ++index) +// str = str.Replace("{" + formatterNames[index] + "}", "{" + index.ToString() + "}"); +// } +// return str; +// } +// +// internal static string MatchAllContentTypeIsNotAllowed +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (MatchAllContentTypeIsNotAllowed), (string) null); +// } +// } +// +// internal static string FormatMatchAllContentTypeIsNotAllowed(object p0) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("MatchAllContentTypeIsNotAllowed", (string) null), p0); +// } +// +// internal static string ObjectResult_MatchAllContentType +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (ObjectResult_MatchAllContentType), (string) null); +// } +// } +// +// internal static string FormatObjectResult_MatchAllContentType(object p0, object p1) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ObjectResult_MatchAllContentType", (string) null), p0, p1); +// } +// +// internal static string ActionExecutor_WrappedTaskInstance +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (ActionExecutor_WrappedTaskInstance), (string) null); +// } +// } +// +// internal static string FormatActionExecutor_WrappedTaskInstance( +// object p0, +// object p1, +// object p2) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ActionExecutor_WrappedTaskInstance", (string) null), p0, p1, p2); +// } +// +// internal static string ActionExecutor_UnexpectedTaskInstance +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (ActionExecutor_UnexpectedTaskInstance), (string) null); +// } +// } +// +// internal static string FormatActionExecutor_UnexpectedTaskInstance(object p0, object p1) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ActionExecutor_UnexpectedTaskInstance", (string) null), p0, p1); +// } +// +// internal static string ActionInvokerFactory_CouldNotCreateInvoker +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (ActionInvokerFactory_CouldNotCreateInvoker), (string) null); +// } +// } +// +// internal static string FormatActionInvokerFactory_CouldNotCreateInvoker(object p0) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ActionInvokerFactory_CouldNotCreateInvoker", (string) null), p0); +// } +// +// internal static string ActionDescriptorMustBeBasedOnControllerAction +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (ActionDescriptorMustBeBasedOnControllerAction), (string) null); +// } +// } +// +// internal static string FormatActionDescriptorMustBeBasedOnControllerAction(object p0) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ActionDescriptorMustBeBasedOnControllerAction", (string) null), p0); +// } +// +// internal static string ArgumentCannotBeNullOrEmpty +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (ArgumentCannotBeNullOrEmpty), (string) null); +// } +// } +// +// internal static string PropertyOfTypeCannotBeNull +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (PropertyOfTypeCannotBeNull), (string) null); +// } +// } +// +// internal static string FormatPropertyOfTypeCannotBeNull(object p0, object p1) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("PropertyOfTypeCannotBeNull", (string) null), p0, p1); +// } +// +// internal static string TypeMethodMustReturnNotNullValue +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (TypeMethodMustReturnNotNullValue), (string) null); +// } +// } +// +// internal static string FormatTypeMethodMustReturnNotNullValue(object p0, object p1) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("TypeMethodMustReturnNotNullValue", (string) null), p0, p1); +// } +// +// internal static string ModelBinding_NullValueNotValid +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (ModelBinding_NullValueNotValid), (string) null); +// } +// } +// +// internal static string FormatModelBinding_NullValueNotValid(object p0) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ModelBinding_NullValueNotValid", (string) null), p0); +// } +// +// internal static string Invalid_IncludePropertyExpression +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (Invalid_IncludePropertyExpression), (string) null); +// } +// } +// +// internal static string FormatInvalid_IncludePropertyExpression(object p0) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("Invalid_IncludePropertyExpression", (string) null), p0); +// } +// +// internal static string NoRoutesMatched +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (NoRoutesMatched), (string) null); +// } +// } +// +// internal static string AsyncActionFilter_InvalidShortCircuit +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (AsyncActionFilter_InvalidShortCircuit), (string) null); +// } +// } +// +// internal static string FormatAsyncActionFilter_InvalidShortCircuit( +// object p0, +// object p1, +// object p2, +// object p3) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("AsyncActionFilter_InvalidShortCircuit", (string) null), p0, p1, p2, p3); +// } +// +// internal static string AsyncResultFilter_InvalidShortCircuit +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (AsyncResultFilter_InvalidShortCircuit), (string) null); +// } +// } +// +// internal static string FormatAsyncResultFilter_InvalidShortCircuit( +// object p0, +// object p1, +// object p2, +// object p3) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("AsyncResultFilter_InvalidShortCircuit", (string) null), p0, p1, p2, p3); +// } +// +// internal static string FilterFactoryAttribute_TypeMustImplementIFilter +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (FilterFactoryAttribute_TypeMustImplementIFilter), (string) null); +// } +// } +// +// internal static string FormatFilterFactoryAttribute_TypeMustImplementIFilter( +// object p0, +// object p1) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("FilterFactoryAttribute_TypeMustImplementIFilter", (string) null), p0, p1); +// } +// +// internal static string ActionResult_ActionReturnValueCannotBeNull +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (ActionResult_ActionReturnValueCannotBeNull), (string) null); +// } +// } +// +// internal static string FormatActionResult_ActionReturnValueCannotBeNull(object p0) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ActionResult_ActionReturnValueCannotBeNull", (string) null), p0); +// } +// +// internal static string TypeMustDeriveFromType +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (TypeMustDeriveFromType), (string) null); +// } +// } +// +// internal static string FormatTypeMustDeriveFromType(object p0, object p1) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("TypeMustDeriveFromType", (string) null), p0, p1); +// } +// +// internal static string InputFormatterNoEncoding +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (InputFormatterNoEncoding), (string) null); +// } +// } +// +// internal static string FormatInputFormatterNoEncoding(object p0) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("InputFormatterNoEncoding", (string) null), p0); +// } +// +// internal static string UnsupportedContentType +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (UnsupportedContentType), (string) null); +// } +// } +// +// internal static string FormatUnsupportedContentType(object p0) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("UnsupportedContentType", (string) null), p0); +// } +// +// internal static string OutputFormatterNoMediaType +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (OutputFormatterNoMediaType), (string) null); +// } +// } +// +// internal static string FormatOutputFormatterNoMediaType(object p0) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("OutputFormatterNoMediaType", (string) null), p0); +// } +// +// internal static string AttributeRoute_AggregateErrorMessage +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (AttributeRoute_AggregateErrorMessage), (string) null); +// } +// } +// +// internal static string FormatAttributeRoute_AggregateErrorMessage(object p0, object p1) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("AttributeRoute_AggregateErrorMessage", (string) null), p0, p1); +// } +// +// internal static string AttributeRoute_CannotContainParameter +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (AttributeRoute_CannotContainParameter), (string) null); +// } +// } +// +// internal static string FormatAttributeRoute_CannotContainParameter( +// object p0, +// object p1, +// object p2) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("AttributeRoute_CannotContainParameter", (string) null), p0, p1, p2); +// } +// +// internal static string AttributeRoute_IndividualErrorMessage +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (AttributeRoute_IndividualErrorMessage), (string) null); +// } +// } +// +// internal static string FormatAttributeRoute_IndividualErrorMessage( +// object p0, +// object p1, +// object p2) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("AttributeRoute_IndividualErrorMessage", (string) null), p0, p1, p2); +// } +// +// internal static string AttributeRoute_TokenReplacement_EmptyTokenNotAllowed +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (AttributeRoute_TokenReplacement_EmptyTokenNotAllowed), (string) null); +// } +// } +// +// internal static string AttributeRoute_TokenReplacement_ImbalancedSquareBrackets +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (AttributeRoute_TokenReplacement_ImbalancedSquareBrackets), (string) null); +// } +// } +// +// internal static string AttributeRoute_TokenReplacement_InvalidSyntax +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (AttributeRoute_TokenReplacement_InvalidSyntax), (string) null); +// } +// } +// +// internal static string FormatAttributeRoute_TokenReplacement_InvalidSyntax(object p0, object p1) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("AttributeRoute_TokenReplacement_InvalidSyntax", (string) null), p0, p1); +// } +// +// internal static string AttributeRoute_TokenReplacement_ReplacementValueNotFound +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (AttributeRoute_TokenReplacement_ReplacementValueNotFound), (string) null); +// } +// } +// +// internal static string FormatAttributeRoute_TokenReplacement_ReplacementValueNotFound( +// object p0, +// object p1, +// object p2) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("AttributeRoute_TokenReplacement_ReplacementValueNotFound", (string) null), p0, p1, p2); +// } +// +// internal static string AttributeRoute_TokenReplacement_UnclosedToken +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (AttributeRoute_TokenReplacement_UnclosedToken), (string) null); +// } +// } +// +// internal static string AttributeRoute_TokenReplacement_UnescapedBraceInToken +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (AttributeRoute_TokenReplacement_UnescapedBraceInToken), (string) null); +// } +// } +// +// internal static string UnableToFindServices +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (UnableToFindServices), (string) null); +// } +// } +// +// internal static string FormatUnableToFindServices(object p0, object p1, object p2) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("UnableToFindServices", (string) null), p0, p1, p2); +// } +// +// internal static string AttributeRoute_DuplicateNames_Item +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (AttributeRoute_DuplicateNames_Item), (string) null); +// } +// } +// +// internal static string FormatAttributeRoute_DuplicateNames_Item(object p0, object p1) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("AttributeRoute_DuplicateNames_Item", (string) null), p0, p1); +// } +// +// internal static string AttributeRoute_DuplicateNames +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (AttributeRoute_DuplicateNames), (string) null); +// } +// } +// +// internal static string FormatAttributeRoute_DuplicateNames(object p0, object p1, object p2) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("AttributeRoute_DuplicateNames", (string) null), p0, p1, p2); +// } +// +// internal static string AttributeRoute_AggregateErrorMessage_ErrorNumber +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (AttributeRoute_AggregateErrorMessage_ErrorNumber), (string) null); +// } +// } +// +// internal static string FormatAttributeRoute_AggregateErrorMessage_ErrorNumber( +// object p0, +// object p1, +// object p2) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("AttributeRoute_AggregateErrorMessage_ErrorNumber", (string) null), p0, p1, p2); +// } +// +// internal static string AttributeRoute_MixedAttributeAndConventionallyRoutedActions_ForMethod +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (AttributeRoute_MixedAttributeAndConventionallyRoutedActions_ForMethod), (string) null); +// } +// } +// +// internal static string FormatAttributeRoute_MixedAttributeAndConventionallyRoutedActions_ForMethod( +// object p0, +// object p1, +// object p2) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("AttributeRoute_MixedAttributeAndConventionallyRoutedActions_ForMethod", (string) null), p0, p1, p2); +// } +// +// internal static string AttributeRoute_MixedAttributeAndConventionallyRoutedActions_ForMethod_Item +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (AttributeRoute_MixedAttributeAndConventionallyRoutedActions_ForMethod_Item), (string) null); +// } +// } +// +// internal static string FormatAttributeRoute_MixedAttributeAndConventionallyRoutedActions_ForMethod_Item( +// object p0, +// object p1, +// object p2) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("AttributeRoute_MixedAttributeAndConventionallyRoutedActions_ForMethod_Item", (string) null), p0, p1, p2); +// } +// +// internal static string AttributeRoute_NullTemplateRepresentation +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (AttributeRoute_NullTemplateRepresentation), (string) null); +// } +// } +// +// internal static string DefaultActionSelector_AmbiguousActions +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (DefaultActionSelector_AmbiguousActions), (string) null); +// } +// } +// +// internal static string FormatDefaultActionSelector_AmbiguousActions(object p0, object p1) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("DefaultActionSelector_AmbiguousActions", (string) null), p0, p1); +// } +// +// internal static string FileResult_InvalidPath +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (FileResult_InvalidPath), (string) null); +// } +// } +// +// internal static string FormatFileResult_InvalidPath(object p0) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("FileResult_InvalidPath", (string) null), p0); +// } +// +// internal static string SerializableError_DefaultError +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (SerializableError_DefaultError), (string) null); +// } +// } +// +// internal static string AsyncResourceFilter_InvalidShortCircuit +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (AsyncResourceFilter_InvalidShortCircuit), (string) null); +// } +// } +// +// internal static string FormatAsyncResourceFilter_InvalidShortCircuit( +// object p0, +// object p1, +// object p2, +// object p3) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("AsyncResourceFilter_InvalidShortCircuit", (string) null), p0, p1, p2, p3); +// } +// +// internal static string ResponseCache_SpecifyDuration +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (ResponseCache_SpecifyDuration), (string) null); +// } +// } +// +// internal static string FormatResponseCache_SpecifyDuration(object p0, object p1) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ResponseCache_SpecifyDuration", (string) null), p0, p1); +// } +// +// internal static string ApiExplorer_UnsupportedAction +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (ApiExplorer_UnsupportedAction), (string) null); +// } +// } +// +// internal static string FormatApiExplorer_UnsupportedAction(object p0) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ApiExplorer_UnsupportedAction", (string) null), p0); +// } +// +// internal static string FormatterMappings_NotValidMediaType +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (FormatterMappings_NotValidMediaType), (string) null); +// } +// } +// +// internal static string FormatFormatterMappings_NotValidMediaType(object p0) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("FormatterMappings_NotValidMediaType", (string) null), p0); +// } +// +// internal static string Format_NotValid +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (Format_NotValid), (string) null); +// } +// } +// +// internal static string FormatFormat_NotValid(object p0) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("Format_NotValid", (string) null), p0); +// } +// +// internal static string CacheProfileNotFound +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (CacheProfileNotFound), (string) null); +// } +// } +// +// internal static string FormatCacheProfileNotFound(object p0) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("CacheProfileNotFound", (string) null), p0); +// } +// +// internal static string ModelType_WrongType +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (ModelType_WrongType), (string) null); +// } +// } +// +// internal static string FormatModelType_WrongType(object p0, object p1) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ModelType_WrongType", (string) null), p0, p1); +// } +// +// internal static string ValueInterfaceAbstractOrOpenGenericTypesCannotBeActivated +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (ValueInterfaceAbstractOrOpenGenericTypesCannotBeActivated), (string) null); +// } +// } +// +// internal static string FormatValueInterfaceAbstractOrOpenGenericTypesCannotBeActivated( +// object p0, +// object p1) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ValueInterfaceAbstractOrOpenGenericTypesCannotBeActivated", (string) null), p0, p1); +// } +// +// internal static string BinderType_MustBeIModelBinder +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (BinderType_MustBeIModelBinder), (string) null); +// } +// } +// +// internal static string FormatBinderType_MustBeIModelBinder(object p0, object p1) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("BinderType_MustBeIModelBinder", (string) null), p0, p1); +// } +// +// internal static string BindingSource_CannotBeComposite +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (BindingSource_CannotBeComposite), (string) null); +// } +// } +// +// internal static string FormatBindingSource_CannotBeComposite(object p0, object p1) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("BindingSource_CannotBeComposite", (string) null), p0, p1); +// } +// +// internal static string BindingSource_CannotBeGreedy +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (BindingSource_CannotBeGreedy), (string) null); +// } +// } +// +// internal static string FormatBindingSource_CannotBeGreedy(object p0, object p1) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("BindingSource_CannotBeGreedy", (string) null), p0, p1); +// } +// +// internal static string Common_PropertyNotFound +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (Common_PropertyNotFound), (string) null); +// } +// } +// +// internal static string FormatCommon_PropertyNotFound(object p0, object p1) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("Common_PropertyNotFound", (string) null), p0, p1); +// } +// +// internal static string JQueryFormValueProviderFactory_MissingClosingBracket +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (JQueryFormValueProviderFactory_MissingClosingBracket), (string) null); +// } +// } +// +// internal static string FormatJQueryFormValueProviderFactory_MissingClosingBracket(object p0) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("JQueryFormValueProviderFactory_MissingClosingBracket", (string) null), p0); +// } +// +// internal static string KeyValuePair_BothKeyAndValueMustBePresent +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (KeyValuePair_BothKeyAndValueMustBePresent), (string) null); +// } +// } +// +// internal static string ModelBinderUtil_ModelCannotBeNull +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (ModelBinderUtil_ModelCannotBeNull), (string) null); +// } +// } +// +// internal static string FormatModelBinderUtil_ModelCannotBeNull(object p0) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ModelBinderUtil_ModelCannotBeNull", (string) null), p0); +// } +// +// internal static string ModelBinderUtil_ModelInstanceIsWrong +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (ModelBinderUtil_ModelInstanceIsWrong), (string) null); +// } +// } +// +// internal static string FormatModelBinderUtil_ModelInstanceIsWrong(object p0, object p1) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ModelBinderUtil_ModelInstanceIsWrong", (string) null), p0, p1); +// } +// +// internal static string ModelBinderUtil_ModelMetadataCannotBeNull +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (ModelBinderUtil_ModelMetadataCannotBeNull), (string) null); +// } +// } +// +// internal static string ModelBinding_MissingBindRequiredMember +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (ModelBinding_MissingBindRequiredMember), (string) null); +// } +// } +// +// internal static string FormatModelBinding_MissingBindRequiredMember(object p0) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ModelBinding_MissingBindRequiredMember", (string) null), p0); +// } +// +// internal static string ModelBinding_MissingRequestBodyRequiredMember +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (ModelBinding_MissingRequestBodyRequiredMember), (string) null); +// } +// } +// +// internal static string ValueProviderResult_NoConverterExists +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (ValueProviderResult_NoConverterExists), (string) null); +// } +// } +// +// internal static string FormatValueProviderResult_NoConverterExists(object p0, object p1) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ValueProviderResult_NoConverterExists", (string) null), p0, p1); +// } +// +// internal static string FileResult_PathNotRooted +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (FileResult_PathNotRooted), (string) null); +// } +// } +// +// internal static string FormatFileResult_PathNotRooted(object p0) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("FileResult_PathNotRooted", (string) null), p0); +// } +// +// internal static string UrlNotLocal +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (UrlNotLocal), (string) null); +// } +// } +// +// internal static string FormatFormatterMappings_GetMediaTypeMappingForFormat_InvalidFormat +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (FormatFormatterMappings_GetMediaTypeMappingForFormat_InvalidFormat), (string) null); +// } +// } +// +// internal static string FormatFormatFormatterMappings_GetMediaTypeMappingForFormat_InvalidFormat( +// object p0) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("FormatFormatterMappings_GetMediaTypeMappingForFormat_InvalidFormat", (string) null), p0); +// } +// +// internal static string AcceptHeaderParser_ParseAcceptHeader_InvalidValues +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (AcceptHeaderParser_ParseAcceptHeader_InvalidValues), (string) null); +// } +// } +// +// internal static string FormatAcceptHeaderParser_ParseAcceptHeader_InvalidValues(object p0) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("AcceptHeaderParser_ParseAcceptHeader_InvalidValues", (string) null), p0); +// } +// +// internal static string ModelState_AttemptedValueIsInvalid +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (ModelState_AttemptedValueIsInvalid), (string) null); +// } +// } +// +// internal static string FormatModelState_AttemptedValueIsInvalid(object p0, object p1) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ModelState_AttemptedValueIsInvalid", (string) null), p0, p1); +// } +// +// internal static string ModelState_NonPropertyAttemptedValueIsInvalid +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (ModelState_NonPropertyAttemptedValueIsInvalid), (string) null); +// } +// } +// +// internal static string FormatModelState_NonPropertyAttemptedValueIsInvalid(object p0) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ModelState_NonPropertyAttemptedValueIsInvalid", (string) null), p0); +// } +// +// internal static string ModelState_UnknownValueIsInvalid +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (ModelState_UnknownValueIsInvalid), (string) null); +// } +// } +// +// internal static string FormatModelState_UnknownValueIsInvalid(object p0) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ModelState_UnknownValueIsInvalid", (string) null), p0); +// } +// +// internal static string ModelState_NonPropertyUnknownValueIsInvalid +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (ModelState_NonPropertyUnknownValueIsInvalid), (string) null); +// } +// } +// +// internal static string HtmlGeneration_ValueIsInvalid +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (HtmlGeneration_ValueIsInvalid), (string) null); +// } +// } +// +// internal static string FormatHtmlGeneration_ValueIsInvalid(object p0) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("HtmlGeneration_ValueIsInvalid", (string) null), p0); +// } +// +// internal static string HtmlGeneration_ValueMustBeNumber +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (HtmlGeneration_ValueMustBeNumber), (string) null); +// } +// } +// +// internal static string FormatHtmlGeneration_ValueMustBeNumber(object p0) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("HtmlGeneration_ValueMustBeNumber", (string) null), p0); +// } +// +// internal static string HtmlGeneration_NonPropertyValueMustBeNumber +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (HtmlGeneration_NonPropertyValueMustBeNumber), (string) null); +// } +// } +// +// internal static string TextInputFormatter_SupportedEncodingsMustNotBeEmpty +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (TextInputFormatter_SupportedEncodingsMustNotBeEmpty), (string) null); +// } +// } +// +// internal static string FormatTextInputFormatter_SupportedEncodingsMustNotBeEmpty(object p0) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("TextInputFormatter_SupportedEncodingsMustNotBeEmpty", (string) null), p0); +// } +// +// internal static string TextOutputFormatter_SupportedEncodingsMustNotBeEmpty +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (TextOutputFormatter_SupportedEncodingsMustNotBeEmpty), (string) null); +// } +// } +// +// internal static string FormatTextOutputFormatter_SupportedEncodingsMustNotBeEmpty(object p0) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("TextOutputFormatter_SupportedEncodingsMustNotBeEmpty", (string) null), p0); +// } +// +// internal static string TextOutputFormatter_WriteResponseBodyAsyncNotSupported +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (TextOutputFormatter_WriteResponseBodyAsyncNotSupported), (string) null); +// } +// } +// +// internal static string FormatTextOutputFormatter_WriteResponseBodyAsyncNotSupported( +// object p0, +// object p1, +// object p2) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("TextOutputFormatter_WriteResponseBodyAsyncNotSupported", (string) null), p0, p1, p2); +// } +// +// internal static string Formatter_NoMediaTypes +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (Formatter_NoMediaTypes), (string) null); +// } +// } +// +// internal static string FormatFormatter_NoMediaTypes(object p0, object p1) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("Formatter_NoMediaTypes", (string) null), p0, p1); +// } +// +// internal static string CouldNotCreateIModelBinder +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (CouldNotCreateIModelBinder), (string) null); +// } +// } +// +// internal static string FormatCouldNotCreateIModelBinder(object p0) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("CouldNotCreateIModelBinder", (string) null), p0); +// } +// +// internal static string InputFormattersAreRequired +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (InputFormattersAreRequired), (string) null); +// } +// } +// +// internal static string FormatInputFormattersAreRequired(object p0, object p1, object p2) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("InputFormattersAreRequired", (string) null), p0, p1, p2); +// } +// +// internal static string ModelBinderProvidersAreRequired +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (ModelBinderProvidersAreRequired), (string) null); +// } +// } +// +// internal static string FormatModelBinderProvidersAreRequired(object p0, object p1, object p2) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ModelBinderProvidersAreRequired", (string) null), p0, p1, p2); +// } +// +// internal static string OutputFormattersAreRequired +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (OutputFormattersAreRequired), (string) null); +// } +// } +// +// internal static string FormatOutputFormattersAreRequired(object p0, object p1, object p2) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("OutputFormattersAreRequired", (string) null), p0, p1, p2); +// } +// +// internal static string MiddewareFilter_ConfigureMethodOverload +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (MiddewareFilter_ConfigureMethodOverload), (string) null); +// } +// } +// +// internal static string FormatMiddewareFilter_ConfigureMethodOverload(object p0) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("MiddewareFilter_ConfigureMethodOverload", (string) null), p0); +// } +// +// internal static string MiddewareFilter_NoConfigureMethod +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (MiddewareFilter_NoConfigureMethod), (string) null); +// } +// } +// +// internal static string FormatMiddewareFilter_NoConfigureMethod(object p0, object p1) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("MiddewareFilter_NoConfigureMethod", (string) null), p0, p1); +// } +// +// internal static string MiddlewareFilterBuilder_NoMiddlewareFeature +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (MiddlewareFilterBuilder_NoMiddlewareFeature), (string) null); +// } +// } +// +// internal static string FormatMiddlewareFilterBuilder_NoMiddlewareFeature(object p0) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("MiddlewareFilterBuilder_NoMiddlewareFeature", (string) null), p0); +// } +// +// internal static string MiddlewareFilterBuilder_NullApplicationBuilder +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (MiddlewareFilterBuilder_NullApplicationBuilder), (string) null); +// } +// } +// +// internal static string FormatMiddlewareFilterBuilder_NullApplicationBuilder(object p0) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("MiddlewareFilterBuilder_NullApplicationBuilder", (string) null), p0); +// } +// +// internal static string MiddlewareFilter_InvalidConfigureReturnType +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (MiddlewareFilter_InvalidConfigureReturnType), (string) null); +// } +// } +// +// internal static string FormatMiddlewareFilter_InvalidConfigureReturnType( +// object p0, +// object p1, +// object p2) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("MiddlewareFilter_InvalidConfigureReturnType", (string) null), p0, p1, p2); +// } +// +// internal static string MiddlewareFilter_ServiceResolutionFail +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (MiddlewareFilter_ServiceResolutionFail), (string) null); +// } +// } +// +// internal static string FormatMiddlewareFilter_ServiceResolutionFail( +// object p0, +// object p1, +// object p2, +// object p3) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("MiddlewareFilter_ServiceResolutionFail", (string) null), p0, p1, p2, p3); +// } +// +// internal static string AuthorizeFilter_AuthorizationPolicyCannotBeCreated +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (AuthorizeFilter_AuthorizationPolicyCannotBeCreated), (string) null); +// } +// } +// +// internal static string FormatAuthorizeFilter_AuthorizationPolicyCannotBeCreated( +// object p0, +// object p1) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("AuthorizeFilter_AuthorizationPolicyCannotBeCreated", (string) null), p0, p1); +// } +// +// internal static string FormCollectionModelBinder_CannotBindToFormCollection +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (FormCollectionModelBinder_CannotBindToFormCollection), (string) null); +// } +// } +// +// internal static string FormatFormCollectionModelBinder_CannotBindToFormCollection( +// object p0, +// object p1, +// object p2) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("FormCollectionModelBinder_CannotBindToFormCollection", (string) null), p0, p1, p2); +// } +// +// internal static string VaryByQueryKeys_Requires_ResponseCachingMiddleware +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (VaryByQueryKeys_Requires_ResponseCachingMiddleware), (string) null); +// } +// } +// +// internal static string FormatVaryByQueryKeys_Requires_ResponseCachingMiddleware(object p0) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("VaryByQueryKeys_Requires_ResponseCachingMiddleware", (string) null), p0); +// } +// +// internal static string CandidateResolver_DifferentCasedReference +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (CandidateResolver_DifferentCasedReference), (string) null); +// } +// } +// +// internal static string FormatCandidateResolver_DifferentCasedReference(object p0) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("CandidateResolver_DifferentCasedReference", (string) null), p0); +// } +// +// internal static string MiddlewareFilterConfigurationProvider_CreateConfigureDelegate_CannotCreateType +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (MiddlewareFilterConfigurationProvider_CreateConfigureDelegate_CannotCreateType), (string) null); +// } +// } +// +// internal static string FormatMiddlewareFilterConfigurationProvider_CreateConfigureDelegate_CannotCreateType( +// object p0, +// object p1) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("MiddlewareFilterConfigurationProvider_CreateConfigureDelegate_CannotCreateType", (string) null), p0, p1); +// } +// +// internal static string Argument_InvalidOffsetLength +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (Argument_InvalidOffsetLength), (string) null); +// } +// } +// +// internal static string FormatArgument_InvalidOffsetLength(object p0, object p1) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("Argument_InvalidOffsetLength", (string) null), p0, p1); +// } +// +// internal static string ComplexTypeModelBinder_NoParameterlessConstructor_ForType +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (ComplexTypeModelBinder_NoParameterlessConstructor_ForType), (string) null); +// } +// } +// +// internal static string FormatComplexTypeModelBinder_NoParameterlessConstructor_ForType(object p0) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ComplexTypeModelBinder_NoParameterlessConstructor_ForType", (string) null), p0); +// } +// +// internal static string ComplexTypeModelBinder_NoParameterlessConstructor_ForProperty +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (ComplexTypeModelBinder_NoParameterlessConstructor_ForProperty), (string) null); +// } +// } +// +// internal static string FormatComplexTypeModelBinder_NoParameterlessConstructor_ForProperty( +// object p0, +// object p1, +// object p2) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ComplexTypeModelBinder_NoParameterlessConstructor_ForProperty", (string) null), p0, p1, p2); +// } +// +// internal static string NoRoutesMatchedForPage +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (NoRoutesMatchedForPage), (string) null); +// } +// } +// +// internal static string FormatNoRoutesMatchedForPage(object p0) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("NoRoutesMatchedForPage", (string) null), p0); +// } +// +// internal static string UrlHelper_RelativePagePathIsNotSupported +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (UrlHelper_RelativePagePathIsNotSupported), (string) null); +// } +// } +// +// internal static string FormatUrlHelper_RelativePagePathIsNotSupported( +// object p0, +// object p1, +// object p2) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("UrlHelper_RelativePagePathIsNotSupported", (string) null), p0, p1, p2); +// } +// +// internal static string ValidationProblemDescription_Title +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (ValidationProblemDescription_Title), (string) null); +// } +// } +// +// internal static string ApiController_AttributeRouteRequired +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (ApiController_AttributeRouteRequired), (string) null); +// } +// } +// +// internal static string FormatApiController_AttributeRouteRequired(object p0, object p1) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ApiController_AttributeRouteRequired", (string) null), p0, p1); +// } +// +// internal static string VirtualFileResultExecutor_NoFileProviderConfigured +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (VirtualFileResultExecutor_NoFileProviderConfigured), (string) null); +// } +// } +// +// internal static string ApplicationPartFactory_InvalidFactoryType +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (ApplicationPartFactory_InvalidFactoryType), (string) null); +// } +// } +// +// internal static string FormatApplicationPartFactory_InvalidFactoryType( +// object p0, +// object p1, +// object p2) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ApplicationPartFactory_InvalidFactoryType", (string) null), p0, p1, p2); +// } +// +// internal static string RelatedAssemblyAttribute_AssemblyCannotReferenceSelf +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (RelatedAssemblyAttribute_AssemblyCannotReferenceSelf), (string) null); +// } +// } +// +// internal static string FormatRelatedAssemblyAttribute_AssemblyCannotReferenceSelf( +// object p0, +// object p1) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("RelatedAssemblyAttribute_AssemblyCannotReferenceSelf", (string) null), p0, p1); +// } +// +// internal static string RelatedAssemblyAttribute_CouldNotBeFound +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (RelatedAssemblyAttribute_CouldNotBeFound), (string) null); +// } +// } +// +// internal static string FormatRelatedAssemblyAttribute_CouldNotBeFound( +// object p0, +// object p1, +// object p2) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("RelatedAssemblyAttribute_CouldNotBeFound", (string) null), p0, p1, p2); +// } +// +// internal static string ApplicationAssembliesProvider_DuplicateRelatedAssembly +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (ApplicationAssembliesProvider_DuplicateRelatedAssembly), (string) null); +// } +// } +// +// internal static string FormatApplicationAssembliesProvider_DuplicateRelatedAssembly(object p0) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ApplicationAssembliesProvider_DuplicateRelatedAssembly", (string) null), p0); +// } +// +// internal static string ApplicationAssembliesProvider_RelatedAssemblyCannotDefineAdditional +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (ApplicationAssembliesProvider_RelatedAssemblyCannotDefineAdditional), (string) null); +// } +// } +// +// internal static string FormatApplicationAssembliesProvider_RelatedAssemblyCannotDefineAdditional( +// object p0, +// object p1) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ApplicationAssembliesProvider_RelatedAssemblyCannotDefineAdditional", (string) null), p0, p1); +// } +// +// internal static string ComplexTypeModelBinder_NoParameterlessConstructor_ForParameter +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (ComplexTypeModelBinder_NoParameterlessConstructor_ForParameter), (string) null); +// } +// } +// +// internal static string FormatComplexTypeModelBinder_NoParameterlessConstructor_ForParameter( +// object p0, +// object p1) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ComplexTypeModelBinder_NoParameterlessConstructor_ForParameter", (string) null), p0, p1); +// } +// +// internal static string ApiController_MultipleBodyParametersFound +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (ApiController_MultipleBodyParametersFound), (string) null); +// } +// } +// +// internal static string FormatApiController_MultipleBodyParametersFound( +// object p0, +// object p1, +// object p2, +// object p3) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ApiController_MultipleBodyParametersFound", (string) null), p0, p1, p2, p3); +// } +// +// internal static string ApiConventionMustBeStatic +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (ApiConventionMustBeStatic), (string) null); +// } +// } +// +// internal static string FormatApiConventionMustBeStatic(object p0) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ApiConventionMustBeStatic", (string) null), p0); +// } +// +// internal static string InvalidTypeTForActionResultOfT +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (InvalidTypeTForActionResultOfT), (string) null); +// } +// } +// +// internal static string FormatInvalidTypeTForActionResultOfT(object p0, object p1) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("InvalidTypeTForActionResultOfT", (string) null), p0, p1); +// } +// +// internal static string ApiConvention_UnsupportedAttributesOnConvention +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (ApiConvention_UnsupportedAttributesOnConvention), (string) null); +// } +// } +// +// internal static string FormatApiConvention_UnsupportedAttributesOnConvention( +// object p0, +// object p1, +// object p2) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ApiConvention_UnsupportedAttributesOnConvention", (string) null), p0, p1, p2); +// } +// +// internal static string ApiConventionMethod_AmbiguousMethodName +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (ApiConventionMethod_AmbiguousMethodName), (string) null); +// } +// } +// +// internal static string FormatApiConventionMethod_AmbiguousMethodName(object p0, object p1) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ApiConventionMethod_AmbiguousMethodName", (string) null), p0, p1); +// } +// +// internal static string ApiConventionMethod_NoMethodFound +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (ApiConventionMethod_NoMethodFound), (string) null); +// } +// } +// +// internal static string FormatApiConventionMethod_NoMethodFound(object p0, object p1) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ApiConventionMethod_NoMethodFound", (string) null), p0, p1); +// } +// +// internal static string ValidationVisitor_ExceededMaxDepth +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (ValidationVisitor_ExceededMaxDepth), (string) null); +// } +// } +// +// internal static string FormatValidationVisitor_ExceededMaxDepth( +// object p0, +// object p1, +// object p2) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ValidationVisitor_ExceededMaxDepth", (string) null), p0, p1, p2); +// } +// +// internal static string ValidationVisitor_ExceededMaxDepthFix +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (ValidationVisitor_ExceededMaxDepthFix), (string) null); +// } +// } +// +// internal static string FormatValidationVisitor_ExceededMaxDepthFix(object p0, object p1) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ValidationVisitor_ExceededMaxDepthFix", (string) null), p0, p1); +// } +// +// internal static string ValidationVisitor_ExceededMaxPropertyDepth +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (ValidationVisitor_ExceededMaxPropertyDepth), (string) null); +// } +// } +// +// internal static string FormatValidationVisitor_ExceededMaxPropertyDepth( +// object p0, +// object p1, +// object p2, +// object p3) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ValidationVisitor_ExceededMaxPropertyDepth", (string) null), p0, p1, p2, p3); +// } +// +// internal static string ApiConventions_Title_400 +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (ApiConventions_Title_400), (string) null); +// } +// } +// +// internal static string ApiConventions_Title_401 +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (ApiConventions_Title_401), (string) null); +// } +// } +// +// internal static string ApiConventions_Title_403 +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (ApiConventions_Title_403), (string) null); +// } +// } +// +// internal static string ApiConventions_Title_404 +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (ApiConventions_Title_404), (string) null); +// } +// } +// +// internal static string ApiConventions_Title_406 +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (ApiConventions_Title_406), (string) null); +// } +// } +// +// internal static string ApiConventions_Title_409 +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (ApiConventions_Title_409), (string) null); +// } +// } +// +// internal static string ApiConventions_Title_415 +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (ApiConventions_Title_415), (string) null); +// } +// } +// +// internal static string ApiConventions_Title_422 +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (ApiConventions_Title_422), (string) null); +// } +// } +// +// internal static string ReferenceToNewtonsoftJsonRequired +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (ReferenceToNewtonsoftJsonRequired), (string) null); +// } +// } +// +// internal static string FormatReferenceToNewtonsoftJsonRequired( +// object p0, +// object p1, +// object p2, +// object p3, +// object p4) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ReferenceToNewtonsoftJsonRequired", (string) null), p0, p1, p2, p3, p4); +// } +// +// internal static string ModelBinding_ExceededMaxModelBindingCollectionSize +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (ModelBinding_ExceededMaxModelBindingCollectionSize), (string) null); +// } +// } +// +// internal static string FormatModelBinding_ExceededMaxModelBindingCollectionSize( +// object p0, +// object p1, +// object p2, +// object p3, +// object p4) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ModelBinding_ExceededMaxModelBindingCollectionSize", (string) null), p0, p1, p2, p3, p4); +// } +// +// internal static string ModelBinding_ExceededMaxModelBindingRecursionDepth +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (ModelBinding_ExceededMaxModelBindingRecursionDepth), (string) null); +// } +// } +// +// internal static string FormatModelBinding_ExceededMaxModelBindingRecursionDepth( +// object p0, +// object p1, +// object p2, +// object p3) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ModelBinding_ExceededMaxModelBindingRecursionDepth", (string) null), p0, p1, p2, p3); +// } +// +// internal static string Property_MustBeInstanceOfType +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (Property_MustBeInstanceOfType), (string) null); +// } +// } +// +// internal static string FormatProperty_MustBeInstanceOfType(object p0, object p1, object p2) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("Property_MustBeInstanceOfType", (string) null), p0, p1, p2); +// } +// +// internal static string ObjectResultExecutor_MaxEnumerationExceeded +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (ObjectResultExecutor_MaxEnumerationExceeded), (string) null); +// } +// } +// +// internal static string FormatObjectResultExecutor_MaxEnumerationExceeded(object p0, object p1) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ObjectResultExecutor_MaxEnumerationExceeded", (string) null), p0, p1); +// } +// +// internal static string UnexpectedJsonEnd +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (UnexpectedJsonEnd), (string) null); +// } +// } +// +// internal static string ApiConventions_Title_500 +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (ApiConventions_Title_500), (string) null); +// } +// } +// +// internal static string FailedToReadRequestForm +// { +// get +// { +// return ResourcesInternal.GetResourceString(nameof (FailedToReadRequestForm), (string) null); +// } +// } +// +// internal static string FormatFailedToReadRequestForm(object p0) +// { +// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("FailedToReadRequestForm", (string) null), p0); +// } +// } +// } diff --git a/src/JsonApiDotNetCore/Configuration/MvcInternals/Strategy.cs b/src/JsonApiDotNetCore/Configuration/MvcInternals/Strategy.cs new file mode 100644 index 0000000000..9eb22ea118 --- /dev/null +++ b/src/JsonApiDotNetCore/Configuration/MvcInternals/Strategy.cs @@ -0,0 +1,249 @@ +// // Copyright (c) .NET Foundation. All rights reserved. +// // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// +// using System; +// using System.Collections; +// using System.Collections.Concurrent; +// using System.Collections.Generic; +// using System.Linq.Expressions; +// using System.Reflection; +// using Microsoft.AspNetCore.Mvc.ModelBinding; +// using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; +// +// namespace JsonApiDotNetCore.Configuration +// { +// internal class JsonApiDefaultComplexObjectValidationStrategy : IValidationStrategy +// { +// private static readonly bool IsMono = Type.GetType("Mono.Runtime") != null; +// +// /// +// /// Gets an instance of . +// /// +// public static readonly IValidationStrategy Instance = new JsonApiDefaultComplexObjectValidationStrategy(); +// +// private JsonApiDefaultComplexObjectValidationStrategy() +// { +// } +// +// /// +// public IEnumerator GetChildren( +// ModelMetadata metadata, +// string key, +// object model) +// { +// return new Enumerator(metadata.Properties, key, model); +// } +// +// private class Enumerator : IEnumerator +// { +// private readonly string _key; +// private readonly object _model; +// private readonly ModelPropertyCollection _properties; +// +// private ValidationEntry _entry; +// private int _index; +// +// public Enumerator( +// ModelPropertyCollection properties, +// string key, +// object model) +// { +// _properties = properties; +// _key = key; +// _model = model; +// +// _index = -1; +// } +// +// public ValidationEntry Current => _entry; +// +// object IEnumerator.Current => Current; +// +// public bool MoveNext() +// { +// _index++; +// if (_index >= _properties.Count) +// { +// return false; +// } +// +// var property = _properties[_index]; +// var propertyName = property.BinderModelName ?? property.PropertyName; +// var key = ModelNames.CreatePropertyModelName(_key, propertyName); +// +// if (_model == null) +// { +// // Performance: Never create a delegate when container is null. +// _entry = new ValidationEntry(property, key, model: null); +// } +// else if (IsMono) +// { +// _entry = new ValidationEntry(property, key, () => GetModelOnMono(_model, property.PropertyName)); +// } +// else +// { +// _entry = new ValidationEntry(property, key, () => GetModel(_model, property)); +// } +// +// return true; +// } +// +// public void Dispose() +// { +// } +// +// public void Reset() +// { +// throw new NotImplementedException(); +// } +// +// private static object GetModel(object container, ModelMetadata property) +// { +// return property.PropertyGetter(container); +// } +// +// // Our property accessors don't work on Mono 4.0.4 - see https://github.com/aspnet/External/issues/44 +// // This is a workaround for what the PropertyGetter does in the background. +// private static object GetModelOnMono(object container, string propertyName) +// { +// var propertyInfo = container.GetType().GetRuntimeProperty(propertyName); +// try +// { +// return propertyInfo.GetValue(container); +// } +// catch (TargetInvocationException ex) +// { +// throw ex.InnerException; +// } +// } +// } +// } +// +// +// /// +// /// The default implementation of for a collection. +// /// +// /// +// /// This implementation handles cases like: +// /// +// /// Model: IList<Student> +// /// Query String: ?students[0].Age=8&students[1].Age=9 +// /// +// /// In this case the elements of the collection are identified in the input data set by an incrementing +// /// integer index. +// /// +// /// +// /// or: +// /// +// /// +// /// Model: IDictionary<string, int> +// /// Query String: ?students[0].Key=Joey&students[0].Value=8 +// /// +// /// In this case the dictionary is treated as a collection of key-value pairs, and the elements of the +// /// collection are identified in the input data set by an incrementing integer index. +// /// +// /// +// /// Using this key format, the enumerator enumerates model objects of type matching +// /// . The indices of the elements in the collection are used to +// /// compute the model prefix keys. +// /// +// internal class JsonApiDefaultCollectionValidationStrategy : IValidationStrategy +// { +// private static readonly MethodInfo _getEnumerator = typeof(JsonApiDefaultCollectionValidationStrategy) +// .GetMethod(nameof(GetEnumerator), BindingFlags.Static | BindingFlags.NonPublic); +// +// /// +// /// Gets an instance of . +// /// +// public static readonly JsonApiDefaultCollectionValidationStrategy Instance = new JsonApiDefaultCollectionValidationStrategy(); +// private readonly ConcurrentDictionary> _genericGetEnumeratorCache = new ConcurrentDictionary>(); +// +// private JsonApiDefaultCollectionValidationStrategy() +// { +// } +// +// /// +// public IEnumerator GetChildren( +// ModelMetadata metadata, +// string key, +// object model) +// { +// var enumerator = GetEnumeratorForElementType(metadata, model); +// return new Enumerator(metadata.ElementMetadata, key, enumerator); +// } +// +// public IEnumerator GetEnumeratorForElementType(ModelMetadata metadata, object model) +// { +// Func getEnumerator = _genericGetEnumeratorCache.GetOrAdd( +// key: metadata.ElementType, +// valueFactory: (type) => { +// var getEnumeratorMethod = _getEnumerator.MakeGenericMethod(type); +// var parameter = Expression.Parameter(typeof(object), "model"); +// var expression = +// Expression.Lambda>( +// Expression.Call(null, getEnumeratorMethod, parameter), +// parameter); +// return expression.Compile(); +// }); +// +// return getEnumerator(model); +// } +// +// // Called via reflection. +// private static IEnumerator GetEnumerator(object model) +// { +// return (model as IEnumerable)?.GetEnumerator() ?? ((IEnumerable)model).GetEnumerator(); +// } +// +// private class Enumerator : IEnumerator +// { +// private readonly string _key; +// private readonly ModelMetadata _metadata; +// private readonly IEnumerator _enumerator; +// +// private ValidationEntry _entry; +// private int _index; +// +// public Enumerator( +// ModelMetadata metadata, +// string key, +// IEnumerator enumerator) +// { +// _metadata = metadata; +// _key = key; +// _enumerator = enumerator; +// +// _index = -1; +// } +// +// public ValidationEntry Current => _entry; +// +// object IEnumerator.Current => Current; +// +// public bool MoveNext() +// { +// _index++; +// if (!_enumerator.MoveNext()) +// { +// return false; +// } +// +// var key = ModelNames.CreateIndexModelName(_key, _index); +// var model = _enumerator.Current; +// +// _entry = new ValidationEntry(_metadata, key, model); +// +// return true; +// } +// +// public void Dispose() +// { +// } +// +// public void Reset() +// { +// _enumerator.Reset(); +// } +// } +// } +// } diff --git a/src/JsonApiDotNetCore/Middleware/HttpContextExtensions.cs b/src/JsonApiDotNetCore/Middleware/HttpContextExtensions.cs index 1e11c9758f..3be172e755 100644 --- a/src/JsonApiDotNetCore/Middleware/HttpContextExtensions.cs +++ b/src/JsonApiDotNetCore/Middleware/HttpContextExtensions.cs @@ -18,6 +18,14 @@ public static bool IsJsonApiRequest(this HttpContext httpContext) string value = httpContext.Items[_isJsonApiRequestKey] as string; return value == bool.TrueString; } + + /// + /// Indicates whether the currently executing HTTP request is a PATCH or POST request. + /// + public static bool IsPatchOrPostRequest(this HttpContext httpContext) + { + return httpContext.Request.Method == HttpMethods.Patch || httpContext.Request.Method == HttpMethods.Post; + } internal static void RegisterJsonApiRequest(this HttpContext httpContext) { @@ -35,7 +43,7 @@ internal static void DisableRequiredValidator(this HttpContext httpContext, stri var itemKey = $"{_disableRequiredValidatorKey}_{model}_{propertyName}"; httpContext.Items[itemKey] = true; } - + internal static bool IsRequiredValidatorDisabled(this HttpContext httpContext, string propertyName, string model) { if (httpContext == null) throw new ArgumentNullException(nameof(httpContext)); diff --git a/src/JsonApiDotNetCore/Serialization/JsonApiReader.cs b/src/JsonApiDotNetCore/Serialization/JsonApiReader.cs index 3ec623964d..e31bd35804 100644 --- a/src/JsonApiDotNetCore/Serialization/JsonApiReader.cs +++ b/src/JsonApiDotNetCore/Serialization/JsonApiReader.cs @@ -78,7 +78,7 @@ public async Task ReadAsync(InputFormatterContext context) private void ValidateIncomingResourceType(InputFormatterContext context, object model) { - if (context.HttpContext.IsJsonApiRequest() && IsPatchOrPostRequest(context.HttpContext.Request)) + if (context.HttpContext.IsJsonApiRequest() && context.HttpContext.IsPatchOrPostRequest()) { var endpointResourceType = GetEndpointResourceType(); if (endpointResourceType == null) @@ -171,11 +171,6 @@ private async Task GetRequestBody(Stream body) return await reader.ReadToEndAsync(); } - private bool IsPatchOrPostRequest(HttpRequest request) - { - return request.Method == HttpMethods.Patch || request.Method == HttpMethods.Post; - } - private IEnumerable GetBodyResourceTypes(object model) { if (model is IEnumerable resourceCollection) diff --git a/src/JsonApiDotNetCore/Serialization/RequestDeserializer.cs b/src/JsonApiDotNetCore/Serialization/RequestDeserializer.cs index 8accaa418e..e9828500af 100644 --- a/src/JsonApiDotNetCore/Serialization/RequestDeserializer.cs +++ b/src/JsonApiDotNetCore/Serialization/RequestDeserializer.cs @@ -73,7 +73,7 @@ protected override IIdentifiable SetAttributes(IIdentifiable resource, IDictiona { if (resource == null) throw new ArgumentNullException(nameof(resource)); if (attributes == null) throw new ArgumentNullException(nameof(attributes)); - + if (_httpContextAccessor.HttpContext.Request.Method == HttpMethod.Patch.Method) { foreach (AttrAttribute attr in attributes) @@ -81,7 +81,7 @@ protected override IIdentifiable SetAttributes(IIdentifiable resource, IDictiona if (attr.Property.GetCustomAttribute() != null) { bool disableValidator = attributeValues == null || !attributeValues.ContainsKey(attr.PublicName); - + if (disableValidator) { _httpContextAccessor.HttpContext.DisableRequiredValidator(attr.Property.Name, resource.GetType().Name); @@ -89,7 +89,7 @@ protected override IIdentifiable SetAttributes(IIdentifiable resource, IDictiona } } } - + return base.SetAttributes(resource, attributeValues, attributes); } } From d4aae4f44c83050a567cf2ffc59aa35353eca363 Mon Sep 17 00:00:00 2001 From: maurei Date: Sat, 3 Oct 2020 13:35:31 +0200 Subject: [PATCH 16/27] feat: implemented --- .../Models/Article.cs | 2 - .../JsonApiDotNetCoreExample/Models/Author.cs | 1 - .../ApplicationBuilderExtensions.cs | 9 - .../JsonApiApplicationBuilder.cs | 16 +- .../JsonApiDataAnnotationsMetadataProvider.cs | 308 --- .../JsonApiModelValidationProvider.cs | 40 - .../MvcInternals/JsonApiValidationStack.cs | 73 - .../MvcInternals/JsonApiValidationVisitor.cs | 376 ---- .../MvcInternals/ResourceInternal.cs | 1680 ----------------- .../Configuration/MvcInternals/Strategy.cs | 249 --- .../JsonApiMetaDataProvider.cs | 7 +- .../JsonApiObjectValidator.cs | 5 +- .../Validation/JsonApiValidationVisitor.cs | 45 + .../PartialPatchValidationFilter.cs} | 5 +- .../Annotations/JsonApiRequiredAttribute.cs | 118 -- 15 files changed, 58 insertions(+), 2876 deletions(-) delete mode 100644 src/JsonApiDotNetCore/Configuration/MvcInternals/JsonApiDataAnnotationsMetadataProvider.cs delete mode 100644 src/JsonApiDotNetCore/Configuration/MvcInternals/JsonApiModelValidationProvider.cs delete mode 100644 src/JsonApiDotNetCore/Configuration/MvcInternals/JsonApiValidationStack.cs delete mode 100644 src/JsonApiDotNetCore/Configuration/MvcInternals/JsonApiValidationVisitor.cs delete mode 100644 src/JsonApiDotNetCore/Configuration/MvcInternals/ResourceInternal.cs delete mode 100644 src/JsonApiDotNetCore/Configuration/MvcInternals/Strategy.cs rename src/JsonApiDotNetCore/Configuration/{MvcInternals => Validation}/JsonApiMetaDataProvider.cs (80%) rename src/JsonApiDotNetCore/Configuration/{MvcInternals => Validation}/JsonApiObjectValidator.cs (95%) create mode 100644 src/JsonApiDotNetCore/Configuration/Validation/JsonApiValidationVisitor.cs rename src/JsonApiDotNetCore/Configuration/{MvcInternals/JsonApiValidationFilter.cs => Validation/PartialPatchValidationFilter.cs} (94%) delete mode 100644 src/JsonApiDotNetCore/Resources/Annotations/JsonApiRequiredAttribute.cs diff --git a/src/Examples/JsonApiDotNetCoreExample/Models/Article.cs b/src/Examples/JsonApiDotNetCoreExample/Models/Article.cs index 357241b54d..49ddc41095 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Models/Article.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Models/Article.cs @@ -9,11 +9,9 @@ namespace JsonApiDotNetCoreExample.Models public sealed class Article : Identifiable { [Attr] - [Required] public string Caption { get; set; } [Attr] - [Required] public string Url { get; set; } [HasOne] diff --git a/src/Examples/JsonApiDotNetCoreExample/Models/Author.cs b/src/Examples/JsonApiDotNetCoreExample/Models/Author.cs index 18b8934a76..f194542043 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Models/Author.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Models/Author.cs @@ -9,7 +9,6 @@ namespace JsonApiDotNetCoreExample.Models public sealed class Author : Identifiable { [Attr] - [Required] public string FirstName { get; set; } [Attr] diff --git a/src/JsonApiDotNetCore/Configuration/ApplicationBuilderExtensions.cs b/src/JsonApiDotNetCore/Configuration/ApplicationBuilderExtensions.cs index 2cbb65d9f0..e7e31ea870 100644 --- a/src/JsonApiDotNetCore/Configuration/ApplicationBuilderExtensions.cs +++ b/src/JsonApiDotNetCore/Configuration/ApplicationBuilderExtensions.cs @@ -43,15 +43,6 @@ public static void UseJsonApi(this IApplicationBuilder builder) var routingConvention = builder.ApplicationServices.GetRequiredService(); options.Conventions.Insert(0, routingConvention); - - - // var validationAttributeAdapterProvider = builder.ApplicationServices.GetRequiredService(); - // var dataAnnotationLocalizationOptions = builder.ApplicationServices.GetRequiredService>(); - // var stringLocalizerFactory = builder.ApplicationServices.GetService(); - // options.ModelValidatorProviders.Add(new DataAnnotationsModelValidatorProvider_COPY(validationAttributeAdapterProvider, dataAnnotationLocalizationOptions, stringLocalizerFactory)); - // options.ModelValidatorProviders.Add(new JsonApiModelValidationProvider()); - // options.ModelMetadataDetailsProviders.Add(new JsonApiDataAnnotationsMetadataProvider()); - }; builder.UseMiddleware(); diff --git a/src/JsonApiDotNetCore/Configuration/JsonApiApplicationBuilder.cs b/src/JsonApiDotNetCore/Configuration/JsonApiApplicationBuilder.cs index e628e63e67..352c933129 100644 --- a/src/JsonApiDotNetCore/Configuration/JsonApiApplicationBuilder.cs +++ b/src/JsonApiDotNetCore/Configuration/JsonApiApplicationBuilder.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using JsonApiDotNetCore.Configuration.Validation; using JsonApiDotNetCore.Hooks.Internal; using JsonApiDotNetCore.Hooks.Internal.Discovery; using JsonApiDotNetCore.Hooks.Internal.Execution; @@ -133,14 +134,6 @@ public void ConfigureServiceContainer(ICollection dbContextTypes) } } - _services.AddSingleton(); - _services.AddSingleton(s => - { - var options = s.GetRequiredService>().Value; - var metadataProvider = s.GetRequiredService(); - return new JsonApiObjectValidator(metadataProvider, options.ModelValidatorProviders, options); - }); - AddResourceLayer(); AddRepositoryLayer(); AddServiceLayer(); @@ -180,6 +173,13 @@ private void AddMiddlewareLayer() _services.AddScoped(); _services.AddScoped(); _services.AddScoped(); + _services.AddSingleton(); + _services.AddSingleton(s => + { + var options = s.GetRequiredService>().Value; + var metadataProvider = s.GetRequiredService(); + return new JsonApiObjectValidator(metadataProvider, options.ModelValidatorProviders, options); + }); } private void AddResourceLayer() diff --git a/src/JsonApiDotNetCore/Configuration/MvcInternals/JsonApiDataAnnotationsMetadataProvider.cs b/src/JsonApiDotNetCore/Configuration/MvcInternals/JsonApiDataAnnotationsMetadataProvider.cs deleted file mode 100644 index dd4bab001c..0000000000 --- a/src/JsonApiDotNetCore/Configuration/MvcInternals/JsonApiDataAnnotationsMetadataProvider.cs +++ /dev/null @@ -1,308 +0,0 @@ -// // Copyright (c) .NET Foundation. All rights reserved. -// // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -// -// using System; -// using System.Collections; -// using System.Collections.Generic; -// using System.ComponentModel; -// using System.ComponentModel.DataAnnotations; -// using System.Diagnostics.Contracts; -// using System.Linq; -// using System.Reflection; -// using System.Runtime.InteropServices; -// using System.Runtime.InteropServices.ComTypes; -// using Microsoft.AspNetCore.Mvc; -// using Microsoft.AspNetCore.Mvc.DataAnnotations; -// using Microsoft.AspNetCore.Mvc.ModelBinding; -// using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata; -// using Microsoft.Extensions.Localization; -// using Microsoft.Extensions.Options; -// -// namespace JsonApiDotNetCore.Configuration -// { -// /// -// /// An implementation of and for -// /// the System.ComponentModel.DataAnnotations attribute classes. -// /// -// internal class JsonApiDataAnnotationsMetadataProvider : IValidationMetadataProvider -// { -// // The [Nullable] attribute is synthesized by the compiler. It's best to just compare the type name. -// private const string NullableAttributeFullTypeName = "System.Runtime.CompilerServices.NullableAttribute"; -// private const string NullableFlagsFieldName = "NullableFlags"; -// private DataAnnotationsMetadataProvider x -// private const string NullableContextAttributeFullName = "System.Runtime.CompilerServices.NullableContextAttribute"; -// private const string NullableContextFlagsFieldName = "Flag"; -// -// private readonly IStringLocalizerFactory _stringLocalizerFactory; -// private readonly MvcOptions _options; -// private readonly MvcDataAnnotationsLocalizationOptions _localizationOptions; -// -// public JsonApiDataAnnotationsMetadataProvider( -// MvcOptions options, -// IOptions localizationOptions, -// IStringLocalizerFactory stringLocalizerFactory) -// { -// if (options == null) -// { -// throw new ArgumentNullException(nameof(options)); -// } -// -// if (localizationOptions == null) -// { -// throw new ArgumentNullException(nameof(localizationOptions)); -// } -// -// _options = options; -// _localizationOptions = localizationOptions.Value; -// _stringLocalizerFactory = stringLocalizerFactory; -// } -// -// -// /// -// public void CreateValidationMetadata(ValidationMetadataProviderContext context) -// { -// if (context == null) -// { -// throw new ArgumentNullException(nameof(context)); -// } -// -// // Read interface .Count once rather than per iteration -// var contextAttributes = context.Attributes; -// var contextAttributesCount = contextAttributes.Count; -// var attributes = new List(contextAttributesCount); -// -// for (var i = 0; i < contextAttributesCount; i++) -// { -// var attribute = contextAttributes[i]; -// if (attribute is ValidationProviderAttribute validationProviderAttribute) -// { -// attributes.AddRange(validationProviderAttribute.GetValidationAttributes()); -// } -// else -// { -// attributes.Add(attribute); -// } -// } -// -// // RequiredAttribute marks a property as required by validation - this means that it -// // must have a non-null value on the model during validation. -// var requiredAttribute = attributes.OfType().FirstOrDefault(); -// -// // For non-nullable reference types, treat them as-if they had an implicit [Required]. -// // This allows the developer to specify [Required] to customize the error message, so -// // if they already have [Required] then there's no need for us to do this check. -// if (!_options.SuppressImplicitRequiredAttributeForNonNullableReferenceTypes && -// requiredAttribute == null && -// !context.Key.ModelType.IsValueType && -// context.Key.MetadataKind != ModelMetadataKind.Type) -// { -// var addInferredRequiredAttribute = false; -// if (context.Key.MetadataKind == ModelMetadataKind.Type) -// { -// // Do nothing. -// } -// else if (context.Key.MetadataKind == ModelMetadataKind.Property) -// { -// var property = context.Key.PropertyInfo; -// if (property is null) -// { -// // PropertyInfo was unavailable on ModelIdentity prior to 3.1. -// // Making a cogent argument about the nullability of the property requires inspecting the declared type, -// // since looking at the runtime type may result in false positives: https://github.com/aspnet/AspNetCore/issues/14812 -// // The only way we could arrive here is if the ModelMetadata was constructed using the non-default provider. -// // We'll cursorily examine the attributes on the property, but not the ContainerType to make a decision about it's nullability. -// -// if (HasNullableAttribute(context.PropertyAttributes, out var propertyHasNullableAttribute)) -// { -// addInferredRequiredAttribute = propertyHasNullableAttribute; -// } -// } -// else -// { -// addInferredRequiredAttribute = IsNullableReferenceType( -// property.DeclaringType, -// member: null, -// context.PropertyAttributes); -// } -// } -// else if (context.Key.MetadataKind == ModelMetadataKind.Parameter) -// { -// addInferredRequiredAttribute = IsNullableReferenceType( -// context.Key.ParameterInfo?.Member.ReflectedType, -// context.Key.ParameterInfo.Member, -// context.ParameterAttributes); -// } -// else -// { -// throw new InvalidOperationException("Unsupported ModelMetadataKind: " + context.Key.MetadataKind); -// } -// -// if (addInferredRequiredAttribute) -// { -// // Since this behavior specifically relates to non-null-ness, we will use the non-default -// // option to tolerate empty/whitespace strings. empty/whitespace INPUT will still result in -// // a validation error by default because we convert empty/whitespace strings to null -// // unless you say otherwise. -// requiredAttribute = new RequiredAttribute() -// { -// AllowEmptyStrings = true, -// }; -// attributes.Add(requiredAttribute); -// } -// } -// -// if (requiredAttribute != null) -// { -// context.ValidationMetadata.IsRequired = true; -// context.ValidationMetadata.PropertyValidationFilter = new JsonApiPartialPatchFilter(); -// } -// -// foreach (var attribute in attributes.OfType()) -// { -// // If another provider has already added this attribute, do not repeat it. -// // This will prevent attributes like RemoteAttribute (which implement ValidationAttribute and -// // IClientModelValidator) to be added to the ValidationMetadata twice. -// // This is to ensure we do not end up with duplication validation rules on the client side. -// if (!context.ValidationMetadata.ValidatorMetadata.Contains(attribute)) -// { -// context.ValidationMetadata.ValidatorMetadata.Add(attribute); -// } -// } -// } -// -// private static string GetDisplayName(FieldInfo field, IStringLocalizer stringLocalizer) -// { -// var display = field.GetCustomAttribute(inherit: false); -// if (display != null) -// { -// // Note [Display(Name = "")] is allowed but we will not attempt to localize the empty name. -// var name = display.GetName(); -// if (stringLocalizer != null && !string.IsNullOrEmpty(name) && display.ResourceType == null) -// { -// name = stringLocalizer[name]; -// } -// -// return name ?? field.Name; -// } -// -// return field.Name; -// } -// -// // Return non-empty group specified in a [Display] attribute for a field, if any; string.Empty otherwise. -// private static string GetDisplayGroup(FieldInfo field) -// { -// var display = field.GetCustomAttribute(inherit: false); -// if (display != null) -// { -// // Note [Display(Group = "")] is allowed. -// var group = display.GetGroupName(); -// if (group != null) -// { -// return group; -// } -// } -// -// return string.Empty; -// } -// -// internal static bool IsNullableReferenceType(Type containingType, MemberInfo member, IEnumerable attributes) -// { -// if (HasNullableAttribute(attributes, out var result)) -// { -// return result; -// } -// -// return IsNullableBasedOnContext(containingType, member); -// } -// -// // Internal for testing -// internal static bool HasNullableAttribute(IEnumerable attributes, out bool isNullable) -// { -// // [Nullable] is compiler synthesized, comparing by name. -// var nullableAttribute = attributes -// .FirstOrDefault(a => string.Equals(a.GetType().FullName, NullableAttributeFullTypeName, StringComparison.Ordinal)); -// if (nullableAttribute == null) -// { -// isNullable = false; -// return false; // [Nullable] not found -// } -// -// // We don't handle cases where generics and NNRT are used. This runs into a -// // fundamental limitation of ModelMetadata - we use a single Type and Property/Parameter -// // to look up the metadata. However when generics are involved and NNRT is in use -// // the distance between the [Nullable] and member we're looking at is potentially -// // unbounded. -// // -// // See: https://github.com/dotnet/roslyn/blob/master/docs/features/nullable-reference-types.md#annotations -// if (nullableAttribute.GetType().GetField(NullableFlagsFieldName) is FieldInfo field && -// field.GetValue(nullableAttribute) is byte[] flags && -// flags.Length >= 0 && -// flags[0] == 1) // First element is the property/parameter type. -// { -// isNullable = true; -// return true; // [Nullable] found and type is an NNRT -// } -// -// isNullable = false; -// return true; // [Nullable] found but type is not an NNRT -// } -// -// internal static bool IsNullableBasedOnContext(Type containingType, MemberInfo member) -// { -// // For generic types, inspecting the nullability requirement additionally requires -// // inspecting the nullability constraint on generic type parameters. This is fairly non-triviial -// // so we'll just avoid calculating it. Users should still be able to apply an explicit [Required] -// // attribute on these members. -// if (containingType.IsGenericType) -// { -// return false; -// } -// -// // The [Nullable] and [NullableContext] attributes are not inherited. -// // -// // The [NullableContext] attribute can appear on a method or on the module. -// var attributes = member?.GetCustomAttributes(inherit: false) ?? Array.Empty(); -// var isNullable = AttributesHasNullableContext(attributes); -// if (isNullable != null) -// { -// return isNullable.Value; -// } -// -// // Check on the containing type -// var type = containingType; -// do -// { -// attributes = type.GetCustomAttributes(inherit: false); -// isNullable = AttributesHasNullableContext(attributes); -// if (isNullable != null) -// { -// return isNullable.Value; -// } -// -// type = type.DeclaringType; -// } -// while (type != null); -// -// // If we don't find the attribute on the declaring type then repeat at the module level -// attributes = containingType.Module.GetCustomAttributes(inherit: false); -// isNullable = AttributesHasNullableContext(attributes); -// return isNullable ?? false; -// -// bool? AttributesHasNullableContext(object[] attributes) -// { -// var nullableContextAttribute = attributes -// .FirstOrDefault(a => string.Equals(a.GetType().FullName, NullableContextAttributeFullName, StringComparison.Ordinal)); -// if (nullableContextAttribute != null) -// { -// if (nullableContextAttribute.GetType().GetField(NullableContextFlagsFieldName) is FieldInfo field && -// field.GetValue(nullableContextAttribute) is byte @byte) -// { -// return @byte == 1; // [NullableContext] found -// } -// } -// -// return null; -// } -// } -// } -// } diff --git a/src/JsonApiDotNetCore/Configuration/MvcInternals/JsonApiModelValidationProvider.cs b/src/JsonApiDotNetCore/Configuration/MvcInternals/JsonApiModelValidationProvider.cs deleted file mode 100644 index d3c44542c9..0000000000 --- a/src/JsonApiDotNetCore/Configuration/MvcInternals/JsonApiModelValidationProvider.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Reflection; -using JsonApiDotNetCore.Resources.Annotations; -using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; - -namespace JsonApiDotNetCore.Configuration -{ - /// - /// This model validator provider does not create any validators, but is used to indirectly change the behavior of - /// the internal DataAnnotationsModelValidatorProvider through the shared object. - /// See https://github.com/json-api-dotnet/JsonApiDotNetCore/pull/847 for more info. - /// - internal sealed class JsonApiModelValidationProvider : IMetadataBasedModelValidatorProvider - { - private static readonly FieldInfo _validatorMetadataBackingField; - - static JsonApiModelValidationProvider() - { - _validatorMetadataBackingField = typeof(ValidatorItem).GetField($"<{nameof(ValidatorItem.ValidatorMetadata)}>k__BackingField", BindingFlags.Instance | BindingFlags.NonPublic); - } - - public void CreateValidators(ModelValidatorProviderContext context) - { - foreach (var item in context.Results) - { - if (item.ValidatorMetadata.GetType() == typeof(RequiredAttribute)) - { - _validatorMetadataBackingField.SetValue(item, new JsonApiRequiredAttribute()); - } - } - } - - /// - /// Returns false to ensure no further validation is executed through this provider. - /// - public bool HasValidators(Type modelType, IList validatorMetadata) => false; - } -} diff --git a/src/JsonApiDotNetCore/Configuration/MvcInternals/JsonApiValidationStack.cs b/src/JsonApiDotNetCore/Configuration/MvcInternals/JsonApiValidationStack.cs deleted file mode 100644 index 40f054a7af..0000000000 --- a/src/JsonApiDotNetCore/Configuration/MvcInternals/JsonApiValidationStack.cs +++ /dev/null @@ -1,73 +0,0 @@ -// // Copyright (c) .NET Foundation. All rights reserved. -// // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -// -// using System.Collections.Generic; -// using System.Diagnostics; -// using Microsoft.EntityFrameworkCore.Internal; -// -// namespace JsonApiDotNetCore.Configuration -// { -// internal class ValidationStack -// { -// public int Count => HashSet?.Count ?? List.Count; -// -// // We tested the performance of a list at size 15 and found it still better than hashset, but to avoid a costly -// // O(n) search at larger n we set the cutoff to 20. If someone finds the point where they intersect feel free to change this number. -// internal const int CutOff = 20; -// -// internal List List { get; } = new List(); -// -// internal HashSet HashSet { get; set; } -// -// public bool Push(object model) -// { -// if (HashSet != null) -// { -// return HashSet.Add(model); -// } -// -// if (ListContains(model)) -// { -// return false; -// } -// -// List.Add(model); -// -// if (HashSet == null && List.Count > CutOff) -// { -// HashSet = new HashSet(List, ReferenceEqualityComparer.Instance); -// } -// -// return true; -// } -// -// public void Pop(object model) -// { -// if (HashSet != null) -// { -// HashSet.Remove(model); -// } -// else -// { -// if (model != null) -// { -// Debug.Assert(ReferenceEquals(List[List.Count - 1], model)); -// List.RemoveAt(List.Count - 1); -// } -// } -// } -// -// private bool ListContains(object model) -// { -// for (var i = 0; i < List.Count; i++) -// { -// if (ReferenceEquals(model, List[i])) -// { -// return true; -// } -// } -// -// return false; -// } -// } -// } diff --git a/src/JsonApiDotNetCore/Configuration/MvcInternals/JsonApiValidationVisitor.cs b/src/JsonApiDotNetCore/Configuration/MvcInternals/JsonApiValidationVisitor.cs deleted file mode 100644 index bb9a665dbf..0000000000 --- a/src/JsonApiDotNetCore/Configuration/MvcInternals/JsonApiValidationVisitor.cs +++ /dev/null @@ -1,376 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Runtime.CompilerServices; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Core; -using Microsoft.AspNetCore.Mvc.ModelBinding; -using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata; -using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; - -namespace JsonApiDotNetCore.Configuration -{ - internal sealed class JsonApiValidationVisitor : ValidationVisitor - { - // private readonly ValidationStack _currentPath; - - public JsonApiValidationVisitor( - ActionContext actionContext, - IModelValidatorProvider validatorProvider, - ValidatorCache validatorCache, - IModelMetadataProvider metadataProvider, - ValidationStateDictionary validationState) - : base(actionContext, validatorProvider, validatorCache, metadataProvider, validationState) { } - // { - // if (actionContext == null) - // { - // throw new ArgumentNullException(nameof(actionContext)); - // } - // - // if (validatorProvider == null) - // { - // throw new ArgumentNullException(nameof(validatorProvider)); - // } - // - // if (validatorCache == null) - // { - // throw new ArgumentNullException(nameof(validatorCache)); - // } - // - // - // _currentPath = new ValidationStack(); - // } - - - protected override bool VisitChildren(IValidationStrategy strategy) - { - var isValid = true; - var enumerator = strategy.GetChildren(Metadata, Key, Model); - var parentEntry = new ValidationEntry(Metadata, Key, Model); - - while (enumerator.MoveNext()) - { - var entry = enumerator.Current; - var metadata = entry.Metadata; - var key = entry.Key; - - var jsonApiFilter = metadata.PropertyValidationFilter as PartialPatchValidationFilter; - var serviceProvider = Context?.HttpContext?.RequestServices; - - if (metadata.PropertyValidationFilter?.ShouldValidateEntry(entry, parentEntry) == false - || jsonApiFilter != null && jsonApiFilter.ShouldValidateEntry(entry, parentEntry, serviceProvider) == false ) - { - SuppressValidation(key); - continue; - } - - isValid &= Visit(metadata, key, entry.Model); - } - - return isValid; - } - - - // /// - // /// Validates a object. - // /// - // /// The associated with the model. - // /// The model prefix key. - // /// The model object. - // /// If true, applies validation rules even if the top-level value is null. - // /// true if the object is valid, otherwise false. - // public override bool Validate(ModelMetadata metadata, string key, object model, bool alwaysValidateAtTopLevel) - // { - // - // if (model == null && key != null && !alwaysValidateAtTopLevel) - // { - // var entry = ModelState[key]; - // - // // Rationale: We might see the same model state key for two different objects and want to preserve any - // // known invalidity. - // if (entry != null && entry.ValidationState != ModelValidationState.Invalid) - // { - // entry.ValidationState = ModelValidationState.Valid; - // } - // - // return true; - // } - // - // return Visit(metadata, key, model); - // } - - // /// - // /// Validates a single node in a model object graph. - // /// - // /// true if the node is valid, otherwise false. - // protected override bool ValidateNode() - // { - // var state = ModelState.GetValidationState(Key); - // - // // Rationale: we might see the same model state key used for two different objects. - // // We want to run validation unless it's already known that this key is invalid. - // if (state != ModelValidationState.Invalid) - // { - // var validators = Cache.GetValidators(Metadata, ValidatorProvider); - // - // var count = validators.Count; - // if (count > 0) - // { - // var context = new ModelValidationContext( - // Context, - // Metadata, - // MetadataProvider, - // Container, - // Model); - // - // var results = new List(); - // for (var i = 0; i < count; i++) - // { - // results.AddRange(validators[i].Validate(context)); - // } - // - // var resultsCount = results.Count; - // for (var i = 0; i < resultsCount; i++) - // { - // var result = results[i]; - // var key = ModelNames.CreatePropertyModelName(Key, result.MemberName); - // - // // It's OK for key to be the empty string here. This can happen when a top - // // level object implements IValidatableObject. - // ModelState.TryAddModelError(key, result.Message); - // } - // } - // } - // - // state = ModelState.GetFieldValidationState(Key); - // if (state == ModelValidationState.Invalid) - // { - // return false; - // } - // else - // { - // // If the field has an entry in ModelState, then record it as valid. Don't create - // // extra entries if they don't exist already. - // var entry = ModelState[Key]; - // if (entry != null) - // { - // entry.ValidationState = ModelValidationState.Valid; - // } - // - // return true; - // } - // } - // - // protected override bool Visit(ModelMetadata metadata, string key, object model) - // { - // RuntimeHelpers.EnsureSufficientExecutionStack(); - // - // if (model != null && !_currentPath.Push(model)) - // { - // // This is a cycle, bail. - // return true; - // } - // - // if (MaxValidationDepth != null && _currentPath.Count > MaxValidationDepth) - // { - // // Non cyclic but too deep an object graph. - // - // // Pop the current model to make ValidationStack.Dispose happy - // _currentPath.Pop(model); - // - // string message; - // switch (metadata.MetadataKind) - // { - // case ModelMetadataKind.Property: - // message = ResourcesInternal.FormatValidationVisitor_ExceededMaxPropertyDepth(nameof(JsonApiValidationVisitor), MaxValidationDepth, metadata.Name, metadata.ContainerType); - // break; - // - // default: - // // Since the minimum depth is never 0, MetadataKind can never be Parameter. Consequently we only special case MetadataKind.Property. - // message = ResourcesInternal.FormatValidationVisitor_ExceededMaxDepth(nameof(JsonApiValidationVisitor), MaxValidationDepth, metadata.ModelType); - // break; - // } - // - // message += " " + ResourcesInternal.FormatValidationVisitor_ExceededMaxDepthFix(nameof(MvcOptions), nameof(MvcOptions.MaxValidationDepth)); - // throw new InvalidOperationException(message) - // { - // HelpLink = "https://aka.ms/AA21ue1", - // }; - // } - // - // var entry = GetValidationEntry(model); - // key = entry?.Key ?? key ?? string.Empty; - // metadata = entry?.Metadata ?? metadata; - // var strategy = entry?.Strategy; - // - // if (ModelState.HasReachedMaxErrors) - // { - // SuppressValidation(key); - // return false; - // } - // else if (entry != null && entry.SuppressValidation) - // { - // // Use the key on the entry, because we might not have entries in model state. - // SuppressValidation(entry.Key); - // _currentPath.Pop(model); - // return true; - // } - // // If the metadata indicates that no validators exist AND the aggregate state for the key says that the model graph - // // is not invalid (i.e. is one of Unvalidated, Valid, or Skipped) we can safely mark the graph as valid. - // else if (metadata.HasValidators == false && - // ModelState.GetFieldValidationState(key) != ModelValidationState.Invalid) - // { - // // No validators will be created for this graph of objects. Mark it as valid if it wasn't previously validated. - // var entries = ModelState.FindKeysWithPrefix(key); - // foreach (var item in entries) - // { - // if (item.Value.ValidationState == ModelValidationState.Unvalidated) - // { - // item.Value.ValidationState = ModelValidationState.Valid; - // } - // } - // - // _currentPath.Pop(model); - // return true; - // } - // - // using (JsonApiStateManager.Recurse(this, key ?? string.Empty, metadata, model, strategy)) - // { - // if (Metadata.IsEnumerableType) - // { - // return VisitComplexType(JsonApiDefaultCollectionValidationStrategy.Instance); - // } - // - // if (Metadata.IsComplexType) - // { - // return VisitComplexType(JsonApiDefaultComplexObjectValidationStrategy.Instance); - // } - // - // return VisitSimpleType(); - // } - // } - // - // // Covers everything VisitSimpleType does not i.e. both enumerations and complex types. - // protected override bool VisitComplexType(IValidationStrategy defaultStrategy) - // { - // var isValid = true; - // if (Model != null && Metadata.ValidateChildren) - // { - // var strategy = Strategy ?? defaultStrategy; - // isValid = VisitChildren(strategy); - // } - // else if (Model != null) - // { - // // Suppress validation for the entries matching this prefix. This will temporarily set - // // the current node to 'skipped' but we're going to visit it right away, so subsequent - // // code will set it to 'valid' or 'invalid' - // SuppressValidation(Key); - // } - // - // // Double-checking HasReachedMaxErrors just in case this model has no properties. - // // If validation has failed for any children, only validate the parent if ValidateComplexTypesIfChildValidationFails is true. - // if ((isValid || ValidateComplexTypesIfChildValidationFails) && !ModelState.HasReachedMaxErrors) - // { - // isValid &= ValidateNode(); - // } - // - // return isValid; - // } - // - // protected override bool VisitSimpleType() - // { - // if (ModelState.HasReachedMaxErrors) - // { - // SuppressValidation(Key); - // return false; - // } - // - // return ValidateNode(); - // } - // - // protected override void SuppressValidation(string key) - // { - // if (key == null) - // { - // // If the key is null, that means that we shouldn't expect any entries in ModelState for - // // this value, so there's nothing to do. - // return; - // } - // - // var entries = ModelState.FindKeysWithPrefix(key); - // foreach (var entry in entries) - // { - // if (entry.Value.ValidationState != ModelValidationState.Invalid) - // { - // entry.Value.ValidationState = ModelValidationState.Skipped; - // } - // } - // } - // - // protected override ValidationStateEntry GetValidationEntry(object model) - // { - // if (model == null || ValidationState == null) - // { - // return null; - // } - // - // ValidationState.TryGetValue(model, out var entry); - // return entry; - // } - // - // protected readonly struct JsonApiStateManager : IDisposable - // { - // private readonly JsonApiValidationVisitor _visitor; - // private readonly object _container; - // private readonly string _key; - // private readonly ModelMetadata _metadata; - // private readonly object _model; - // private readonly object _newModel; - // private readonly IValidationStrategy _strategy; - // - // public static JsonApiStateManager Recurse( - // JsonApiValidationVisitor visitor, - // string key, - // ModelMetadata metadata, - // object model, - // IValidationStrategy strategy) - // { - // var recursifier = new JsonApiStateManager(visitor, model); - // - // visitor.Container = visitor.Model; - // visitor.Key = key; - // visitor.Metadata = metadata; - // visitor.Model = model; - // visitor.Strategy = strategy; - // - // return recursifier; - // } - // - // public JsonApiStateManager(JsonApiValidationVisitor visitor, object newModel) - // { - // _visitor = visitor; - // _newModel = newModel; - // - // _container = _visitor.Container; - // _key = _visitor.Key; - // _metadata = _visitor.Metadata; - // _model = _visitor.Model; - // _strategy = _visitor.Strategy; - // } - // - // public void Dispose() - // { - // _visitor.Container = _container; - // _visitor.Key = _key; - // _visitor.Metadata = _metadata; - // _visitor.Model = _model; - // _visitor.Strategy = _strategy; - // - // _visitor._currentPath.Pop(_newModel); - // } - // } - } -} diff --git a/src/JsonApiDotNetCore/Configuration/MvcInternals/ResourceInternal.cs b/src/JsonApiDotNetCore/Configuration/MvcInternals/ResourceInternal.cs deleted file mode 100644 index 9ae9244524..0000000000 --- a/src/JsonApiDotNetCore/Configuration/MvcInternals/ResourceInternal.cs +++ /dev/null @@ -1,1680 +0,0 @@ -// // Decompiled with JetBrains decompiler -// // Type: ResourcesInternal -// // Assembly: Microsoft.AspNetCore.Mvc.Core, Version=3.1.5.0, Culture=neutral, PublicKeyToken=adb9793829ddae60 -// // MVID: C072ADA3-420F-4E08-8493-043C3240EB59 -// // Assembly location: /usr/local/share/dotnet/shared/Microsoft.AspNetCore.App/3.1.5/Microsoft.AspNetCore.Mvc.Core.dll -// -// using System; -// using System.Globalization; -// using System.Resources; -// using System.Runtime.CompilerServices; -// -// namespace JsonApiDotNetCore.Configuration -// { -// internal static class ResourcesInternal -// { -// private static ResourceManager s_resourceManager; -// -// internal static ResourceManager ResourceManager -// { -// get -// { -// return s_resourceManager ?? (s_resourceManager = new ResourceManager(typeof (ResourcesInternal))); -// } -// } -// -// internal static CultureInfo Culture { get; set; } -// -// [MethodImpl(MethodImplOptions.AggressiveInlining)] -// internal static string GetResourceString(string resourceKey, string defaultValue = null) -// { -// return ResourcesInternal.ResourceManager.GetString(resourceKey, ResourcesInternal.Culture); -// } -// -// private static string GetResourceString(string resourceKey, string[] formatterNames) -// { -// string str = ResourcesInternal.GetResourceString(resourceKey, (string) null); -// if (formatterNames != null) -// { -// for (int index = 0; index < formatterNames.Length; ++index) -// str = str.Replace("{" + formatterNames[index] + "}", "{" + index.ToString() + "}"); -// } -// return str; -// } -// -// internal static string MatchAllContentTypeIsNotAllowed -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (MatchAllContentTypeIsNotAllowed), (string) null); -// } -// } -// -// internal static string FormatMatchAllContentTypeIsNotAllowed(object p0) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("MatchAllContentTypeIsNotAllowed", (string) null), p0); -// } -// -// internal static string ObjectResult_MatchAllContentType -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (ObjectResult_MatchAllContentType), (string) null); -// } -// } -// -// internal static string FormatObjectResult_MatchAllContentType(object p0, object p1) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ObjectResult_MatchAllContentType", (string) null), p0, p1); -// } -// -// internal static string ActionExecutor_WrappedTaskInstance -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (ActionExecutor_WrappedTaskInstance), (string) null); -// } -// } -// -// internal static string FormatActionExecutor_WrappedTaskInstance( -// object p0, -// object p1, -// object p2) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ActionExecutor_WrappedTaskInstance", (string) null), p0, p1, p2); -// } -// -// internal static string ActionExecutor_UnexpectedTaskInstance -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (ActionExecutor_UnexpectedTaskInstance), (string) null); -// } -// } -// -// internal static string FormatActionExecutor_UnexpectedTaskInstance(object p0, object p1) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ActionExecutor_UnexpectedTaskInstance", (string) null), p0, p1); -// } -// -// internal static string ActionInvokerFactory_CouldNotCreateInvoker -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (ActionInvokerFactory_CouldNotCreateInvoker), (string) null); -// } -// } -// -// internal static string FormatActionInvokerFactory_CouldNotCreateInvoker(object p0) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ActionInvokerFactory_CouldNotCreateInvoker", (string) null), p0); -// } -// -// internal static string ActionDescriptorMustBeBasedOnControllerAction -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (ActionDescriptorMustBeBasedOnControllerAction), (string) null); -// } -// } -// -// internal static string FormatActionDescriptorMustBeBasedOnControllerAction(object p0) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ActionDescriptorMustBeBasedOnControllerAction", (string) null), p0); -// } -// -// internal static string ArgumentCannotBeNullOrEmpty -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (ArgumentCannotBeNullOrEmpty), (string) null); -// } -// } -// -// internal static string PropertyOfTypeCannotBeNull -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (PropertyOfTypeCannotBeNull), (string) null); -// } -// } -// -// internal static string FormatPropertyOfTypeCannotBeNull(object p0, object p1) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("PropertyOfTypeCannotBeNull", (string) null), p0, p1); -// } -// -// internal static string TypeMethodMustReturnNotNullValue -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (TypeMethodMustReturnNotNullValue), (string) null); -// } -// } -// -// internal static string FormatTypeMethodMustReturnNotNullValue(object p0, object p1) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("TypeMethodMustReturnNotNullValue", (string) null), p0, p1); -// } -// -// internal static string ModelBinding_NullValueNotValid -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (ModelBinding_NullValueNotValid), (string) null); -// } -// } -// -// internal static string FormatModelBinding_NullValueNotValid(object p0) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ModelBinding_NullValueNotValid", (string) null), p0); -// } -// -// internal static string Invalid_IncludePropertyExpression -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (Invalid_IncludePropertyExpression), (string) null); -// } -// } -// -// internal static string FormatInvalid_IncludePropertyExpression(object p0) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("Invalid_IncludePropertyExpression", (string) null), p0); -// } -// -// internal static string NoRoutesMatched -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (NoRoutesMatched), (string) null); -// } -// } -// -// internal static string AsyncActionFilter_InvalidShortCircuit -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (AsyncActionFilter_InvalidShortCircuit), (string) null); -// } -// } -// -// internal static string FormatAsyncActionFilter_InvalidShortCircuit( -// object p0, -// object p1, -// object p2, -// object p3) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("AsyncActionFilter_InvalidShortCircuit", (string) null), p0, p1, p2, p3); -// } -// -// internal static string AsyncResultFilter_InvalidShortCircuit -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (AsyncResultFilter_InvalidShortCircuit), (string) null); -// } -// } -// -// internal static string FormatAsyncResultFilter_InvalidShortCircuit( -// object p0, -// object p1, -// object p2, -// object p3) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("AsyncResultFilter_InvalidShortCircuit", (string) null), p0, p1, p2, p3); -// } -// -// internal static string FilterFactoryAttribute_TypeMustImplementIFilter -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (FilterFactoryAttribute_TypeMustImplementIFilter), (string) null); -// } -// } -// -// internal static string FormatFilterFactoryAttribute_TypeMustImplementIFilter( -// object p0, -// object p1) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("FilterFactoryAttribute_TypeMustImplementIFilter", (string) null), p0, p1); -// } -// -// internal static string ActionResult_ActionReturnValueCannotBeNull -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (ActionResult_ActionReturnValueCannotBeNull), (string) null); -// } -// } -// -// internal static string FormatActionResult_ActionReturnValueCannotBeNull(object p0) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ActionResult_ActionReturnValueCannotBeNull", (string) null), p0); -// } -// -// internal static string TypeMustDeriveFromType -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (TypeMustDeriveFromType), (string) null); -// } -// } -// -// internal static string FormatTypeMustDeriveFromType(object p0, object p1) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("TypeMustDeriveFromType", (string) null), p0, p1); -// } -// -// internal static string InputFormatterNoEncoding -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (InputFormatterNoEncoding), (string) null); -// } -// } -// -// internal static string FormatInputFormatterNoEncoding(object p0) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("InputFormatterNoEncoding", (string) null), p0); -// } -// -// internal static string UnsupportedContentType -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (UnsupportedContentType), (string) null); -// } -// } -// -// internal static string FormatUnsupportedContentType(object p0) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("UnsupportedContentType", (string) null), p0); -// } -// -// internal static string OutputFormatterNoMediaType -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (OutputFormatterNoMediaType), (string) null); -// } -// } -// -// internal static string FormatOutputFormatterNoMediaType(object p0) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("OutputFormatterNoMediaType", (string) null), p0); -// } -// -// internal static string AttributeRoute_AggregateErrorMessage -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (AttributeRoute_AggregateErrorMessage), (string) null); -// } -// } -// -// internal static string FormatAttributeRoute_AggregateErrorMessage(object p0, object p1) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("AttributeRoute_AggregateErrorMessage", (string) null), p0, p1); -// } -// -// internal static string AttributeRoute_CannotContainParameter -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (AttributeRoute_CannotContainParameter), (string) null); -// } -// } -// -// internal static string FormatAttributeRoute_CannotContainParameter( -// object p0, -// object p1, -// object p2) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("AttributeRoute_CannotContainParameter", (string) null), p0, p1, p2); -// } -// -// internal static string AttributeRoute_IndividualErrorMessage -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (AttributeRoute_IndividualErrorMessage), (string) null); -// } -// } -// -// internal static string FormatAttributeRoute_IndividualErrorMessage( -// object p0, -// object p1, -// object p2) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("AttributeRoute_IndividualErrorMessage", (string) null), p0, p1, p2); -// } -// -// internal static string AttributeRoute_TokenReplacement_EmptyTokenNotAllowed -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (AttributeRoute_TokenReplacement_EmptyTokenNotAllowed), (string) null); -// } -// } -// -// internal static string AttributeRoute_TokenReplacement_ImbalancedSquareBrackets -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (AttributeRoute_TokenReplacement_ImbalancedSquareBrackets), (string) null); -// } -// } -// -// internal static string AttributeRoute_TokenReplacement_InvalidSyntax -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (AttributeRoute_TokenReplacement_InvalidSyntax), (string) null); -// } -// } -// -// internal static string FormatAttributeRoute_TokenReplacement_InvalidSyntax(object p0, object p1) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("AttributeRoute_TokenReplacement_InvalidSyntax", (string) null), p0, p1); -// } -// -// internal static string AttributeRoute_TokenReplacement_ReplacementValueNotFound -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (AttributeRoute_TokenReplacement_ReplacementValueNotFound), (string) null); -// } -// } -// -// internal static string FormatAttributeRoute_TokenReplacement_ReplacementValueNotFound( -// object p0, -// object p1, -// object p2) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("AttributeRoute_TokenReplacement_ReplacementValueNotFound", (string) null), p0, p1, p2); -// } -// -// internal static string AttributeRoute_TokenReplacement_UnclosedToken -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (AttributeRoute_TokenReplacement_UnclosedToken), (string) null); -// } -// } -// -// internal static string AttributeRoute_TokenReplacement_UnescapedBraceInToken -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (AttributeRoute_TokenReplacement_UnescapedBraceInToken), (string) null); -// } -// } -// -// internal static string UnableToFindServices -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (UnableToFindServices), (string) null); -// } -// } -// -// internal static string FormatUnableToFindServices(object p0, object p1, object p2) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("UnableToFindServices", (string) null), p0, p1, p2); -// } -// -// internal static string AttributeRoute_DuplicateNames_Item -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (AttributeRoute_DuplicateNames_Item), (string) null); -// } -// } -// -// internal static string FormatAttributeRoute_DuplicateNames_Item(object p0, object p1) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("AttributeRoute_DuplicateNames_Item", (string) null), p0, p1); -// } -// -// internal static string AttributeRoute_DuplicateNames -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (AttributeRoute_DuplicateNames), (string) null); -// } -// } -// -// internal static string FormatAttributeRoute_DuplicateNames(object p0, object p1, object p2) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("AttributeRoute_DuplicateNames", (string) null), p0, p1, p2); -// } -// -// internal static string AttributeRoute_AggregateErrorMessage_ErrorNumber -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (AttributeRoute_AggregateErrorMessage_ErrorNumber), (string) null); -// } -// } -// -// internal static string FormatAttributeRoute_AggregateErrorMessage_ErrorNumber( -// object p0, -// object p1, -// object p2) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("AttributeRoute_AggregateErrorMessage_ErrorNumber", (string) null), p0, p1, p2); -// } -// -// internal static string AttributeRoute_MixedAttributeAndConventionallyRoutedActions_ForMethod -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (AttributeRoute_MixedAttributeAndConventionallyRoutedActions_ForMethod), (string) null); -// } -// } -// -// internal static string FormatAttributeRoute_MixedAttributeAndConventionallyRoutedActions_ForMethod( -// object p0, -// object p1, -// object p2) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("AttributeRoute_MixedAttributeAndConventionallyRoutedActions_ForMethod", (string) null), p0, p1, p2); -// } -// -// internal static string AttributeRoute_MixedAttributeAndConventionallyRoutedActions_ForMethod_Item -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (AttributeRoute_MixedAttributeAndConventionallyRoutedActions_ForMethod_Item), (string) null); -// } -// } -// -// internal static string FormatAttributeRoute_MixedAttributeAndConventionallyRoutedActions_ForMethod_Item( -// object p0, -// object p1, -// object p2) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("AttributeRoute_MixedAttributeAndConventionallyRoutedActions_ForMethod_Item", (string) null), p0, p1, p2); -// } -// -// internal static string AttributeRoute_NullTemplateRepresentation -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (AttributeRoute_NullTemplateRepresentation), (string) null); -// } -// } -// -// internal static string DefaultActionSelector_AmbiguousActions -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (DefaultActionSelector_AmbiguousActions), (string) null); -// } -// } -// -// internal static string FormatDefaultActionSelector_AmbiguousActions(object p0, object p1) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("DefaultActionSelector_AmbiguousActions", (string) null), p0, p1); -// } -// -// internal static string FileResult_InvalidPath -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (FileResult_InvalidPath), (string) null); -// } -// } -// -// internal static string FormatFileResult_InvalidPath(object p0) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("FileResult_InvalidPath", (string) null), p0); -// } -// -// internal static string SerializableError_DefaultError -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (SerializableError_DefaultError), (string) null); -// } -// } -// -// internal static string AsyncResourceFilter_InvalidShortCircuit -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (AsyncResourceFilter_InvalidShortCircuit), (string) null); -// } -// } -// -// internal static string FormatAsyncResourceFilter_InvalidShortCircuit( -// object p0, -// object p1, -// object p2, -// object p3) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("AsyncResourceFilter_InvalidShortCircuit", (string) null), p0, p1, p2, p3); -// } -// -// internal static string ResponseCache_SpecifyDuration -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (ResponseCache_SpecifyDuration), (string) null); -// } -// } -// -// internal static string FormatResponseCache_SpecifyDuration(object p0, object p1) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ResponseCache_SpecifyDuration", (string) null), p0, p1); -// } -// -// internal static string ApiExplorer_UnsupportedAction -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (ApiExplorer_UnsupportedAction), (string) null); -// } -// } -// -// internal static string FormatApiExplorer_UnsupportedAction(object p0) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ApiExplorer_UnsupportedAction", (string) null), p0); -// } -// -// internal static string FormatterMappings_NotValidMediaType -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (FormatterMappings_NotValidMediaType), (string) null); -// } -// } -// -// internal static string FormatFormatterMappings_NotValidMediaType(object p0) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("FormatterMappings_NotValidMediaType", (string) null), p0); -// } -// -// internal static string Format_NotValid -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (Format_NotValid), (string) null); -// } -// } -// -// internal static string FormatFormat_NotValid(object p0) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("Format_NotValid", (string) null), p0); -// } -// -// internal static string CacheProfileNotFound -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (CacheProfileNotFound), (string) null); -// } -// } -// -// internal static string FormatCacheProfileNotFound(object p0) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("CacheProfileNotFound", (string) null), p0); -// } -// -// internal static string ModelType_WrongType -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (ModelType_WrongType), (string) null); -// } -// } -// -// internal static string FormatModelType_WrongType(object p0, object p1) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ModelType_WrongType", (string) null), p0, p1); -// } -// -// internal static string ValueInterfaceAbstractOrOpenGenericTypesCannotBeActivated -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (ValueInterfaceAbstractOrOpenGenericTypesCannotBeActivated), (string) null); -// } -// } -// -// internal static string FormatValueInterfaceAbstractOrOpenGenericTypesCannotBeActivated( -// object p0, -// object p1) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ValueInterfaceAbstractOrOpenGenericTypesCannotBeActivated", (string) null), p0, p1); -// } -// -// internal static string BinderType_MustBeIModelBinder -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (BinderType_MustBeIModelBinder), (string) null); -// } -// } -// -// internal static string FormatBinderType_MustBeIModelBinder(object p0, object p1) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("BinderType_MustBeIModelBinder", (string) null), p0, p1); -// } -// -// internal static string BindingSource_CannotBeComposite -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (BindingSource_CannotBeComposite), (string) null); -// } -// } -// -// internal static string FormatBindingSource_CannotBeComposite(object p0, object p1) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("BindingSource_CannotBeComposite", (string) null), p0, p1); -// } -// -// internal static string BindingSource_CannotBeGreedy -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (BindingSource_CannotBeGreedy), (string) null); -// } -// } -// -// internal static string FormatBindingSource_CannotBeGreedy(object p0, object p1) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("BindingSource_CannotBeGreedy", (string) null), p0, p1); -// } -// -// internal static string Common_PropertyNotFound -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (Common_PropertyNotFound), (string) null); -// } -// } -// -// internal static string FormatCommon_PropertyNotFound(object p0, object p1) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("Common_PropertyNotFound", (string) null), p0, p1); -// } -// -// internal static string JQueryFormValueProviderFactory_MissingClosingBracket -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (JQueryFormValueProviderFactory_MissingClosingBracket), (string) null); -// } -// } -// -// internal static string FormatJQueryFormValueProviderFactory_MissingClosingBracket(object p0) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("JQueryFormValueProviderFactory_MissingClosingBracket", (string) null), p0); -// } -// -// internal static string KeyValuePair_BothKeyAndValueMustBePresent -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (KeyValuePair_BothKeyAndValueMustBePresent), (string) null); -// } -// } -// -// internal static string ModelBinderUtil_ModelCannotBeNull -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (ModelBinderUtil_ModelCannotBeNull), (string) null); -// } -// } -// -// internal static string FormatModelBinderUtil_ModelCannotBeNull(object p0) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ModelBinderUtil_ModelCannotBeNull", (string) null), p0); -// } -// -// internal static string ModelBinderUtil_ModelInstanceIsWrong -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (ModelBinderUtil_ModelInstanceIsWrong), (string) null); -// } -// } -// -// internal static string FormatModelBinderUtil_ModelInstanceIsWrong(object p0, object p1) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ModelBinderUtil_ModelInstanceIsWrong", (string) null), p0, p1); -// } -// -// internal static string ModelBinderUtil_ModelMetadataCannotBeNull -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (ModelBinderUtil_ModelMetadataCannotBeNull), (string) null); -// } -// } -// -// internal static string ModelBinding_MissingBindRequiredMember -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (ModelBinding_MissingBindRequiredMember), (string) null); -// } -// } -// -// internal static string FormatModelBinding_MissingBindRequiredMember(object p0) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ModelBinding_MissingBindRequiredMember", (string) null), p0); -// } -// -// internal static string ModelBinding_MissingRequestBodyRequiredMember -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (ModelBinding_MissingRequestBodyRequiredMember), (string) null); -// } -// } -// -// internal static string ValueProviderResult_NoConverterExists -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (ValueProviderResult_NoConverterExists), (string) null); -// } -// } -// -// internal static string FormatValueProviderResult_NoConverterExists(object p0, object p1) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ValueProviderResult_NoConverterExists", (string) null), p0, p1); -// } -// -// internal static string FileResult_PathNotRooted -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (FileResult_PathNotRooted), (string) null); -// } -// } -// -// internal static string FormatFileResult_PathNotRooted(object p0) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("FileResult_PathNotRooted", (string) null), p0); -// } -// -// internal static string UrlNotLocal -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (UrlNotLocal), (string) null); -// } -// } -// -// internal static string FormatFormatterMappings_GetMediaTypeMappingForFormat_InvalidFormat -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (FormatFormatterMappings_GetMediaTypeMappingForFormat_InvalidFormat), (string) null); -// } -// } -// -// internal static string FormatFormatFormatterMappings_GetMediaTypeMappingForFormat_InvalidFormat( -// object p0) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("FormatFormatterMappings_GetMediaTypeMappingForFormat_InvalidFormat", (string) null), p0); -// } -// -// internal static string AcceptHeaderParser_ParseAcceptHeader_InvalidValues -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (AcceptHeaderParser_ParseAcceptHeader_InvalidValues), (string) null); -// } -// } -// -// internal static string FormatAcceptHeaderParser_ParseAcceptHeader_InvalidValues(object p0) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("AcceptHeaderParser_ParseAcceptHeader_InvalidValues", (string) null), p0); -// } -// -// internal static string ModelState_AttemptedValueIsInvalid -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (ModelState_AttemptedValueIsInvalid), (string) null); -// } -// } -// -// internal static string FormatModelState_AttemptedValueIsInvalid(object p0, object p1) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ModelState_AttemptedValueIsInvalid", (string) null), p0, p1); -// } -// -// internal static string ModelState_NonPropertyAttemptedValueIsInvalid -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (ModelState_NonPropertyAttemptedValueIsInvalid), (string) null); -// } -// } -// -// internal static string FormatModelState_NonPropertyAttemptedValueIsInvalid(object p0) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ModelState_NonPropertyAttemptedValueIsInvalid", (string) null), p0); -// } -// -// internal static string ModelState_UnknownValueIsInvalid -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (ModelState_UnknownValueIsInvalid), (string) null); -// } -// } -// -// internal static string FormatModelState_UnknownValueIsInvalid(object p0) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ModelState_UnknownValueIsInvalid", (string) null), p0); -// } -// -// internal static string ModelState_NonPropertyUnknownValueIsInvalid -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (ModelState_NonPropertyUnknownValueIsInvalid), (string) null); -// } -// } -// -// internal static string HtmlGeneration_ValueIsInvalid -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (HtmlGeneration_ValueIsInvalid), (string) null); -// } -// } -// -// internal static string FormatHtmlGeneration_ValueIsInvalid(object p0) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("HtmlGeneration_ValueIsInvalid", (string) null), p0); -// } -// -// internal static string HtmlGeneration_ValueMustBeNumber -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (HtmlGeneration_ValueMustBeNumber), (string) null); -// } -// } -// -// internal static string FormatHtmlGeneration_ValueMustBeNumber(object p0) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("HtmlGeneration_ValueMustBeNumber", (string) null), p0); -// } -// -// internal static string HtmlGeneration_NonPropertyValueMustBeNumber -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (HtmlGeneration_NonPropertyValueMustBeNumber), (string) null); -// } -// } -// -// internal static string TextInputFormatter_SupportedEncodingsMustNotBeEmpty -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (TextInputFormatter_SupportedEncodingsMustNotBeEmpty), (string) null); -// } -// } -// -// internal static string FormatTextInputFormatter_SupportedEncodingsMustNotBeEmpty(object p0) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("TextInputFormatter_SupportedEncodingsMustNotBeEmpty", (string) null), p0); -// } -// -// internal static string TextOutputFormatter_SupportedEncodingsMustNotBeEmpty -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (TextOutputFormatter_SupportedEncodingsMustNotBeEmpty), (string) null); -// } -// } -// -// internal static string FormatTextOutputFormatter_SupportedEncodingsMustNotBeEmpty(object p0) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("TextOutputFormatter_SupportedEncodingsMustNotBeEmpty", (string) null), p0); -// } -// -// internal static string TextOutputFormatter_WriteResponseBodyAsyncNotSupported -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (TextOutputFormatter_WriteResponseBodyAsyncNotSupported), (string) null); -// } -// } -// -// internal static string FormatTextOutputFormatter_WriteResponseBodyAsyncNotSupported( -// object p0, -// object p1, -// object p2) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("TextOutputFormatter_WriteResponseBodyAsyncNotSupported", (string) null), p0, p1, p2); -// } -// -// internal static string Formatter_NoMediaTypes -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (Formatter_NoMediaTypes), (string) null); -// } -// } -// -// internal static string FormatFormatter_NoMediaTypes(object p0, object p1) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("Formatter_NoMediaTypes", (string) null), p0, p1); -// } -// -// internal static string CouldNotCreateIModelBinder -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (CouldNotCreateIModelBinder), (string) null); -// } -// } -// -// internal static string FormatCouldNotCreateIModelBinder(object p0) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("CouldNotCreateIModelBinder", (string) null), p0); -// } -// -// internal static string InputFormattersAreRequired -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (InputFormattersAreRequired), (string) null); -// } -// } -// -// internal static string FormatInputFormattersAreRequired(object p0, object p1, object p2) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("InputFormattersAreRequired", (string) null), p0, p1, p2); -// } -// -// internal static string ModelBinderProvidersAreRequired -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (ModelBinderProvidersAreRequired), (string) null); -// } -// } -// -// internal static string FormatModelBinderProvidersAreRequired(object p0, object p1, object p2) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ModelBinderProvidersAreRequired", (string) null), p0, p1, p2); -// } -// -// internal static string OutputFormattersAreRequired -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (OutputFormattersAreRequired), (string) null); -// } -// } -// -// internal static string FormatOutputFormattersAreRequired(object p0, object p1, object p2) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("OutputFormattersAreRequired", (string) null), p0, p1, p2); -// } -// -// internal static string MiddewareFilter_ConfigureMethodOverload -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (MiddewareFilter_ConfigureMethodOverload), (string) null); -// } -// } -// -// internal static string FormatMiddewareFilter_ConfigureMethodOverload(object p0) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("MiddewareFilter_ConfigureMethodOverload", (string) null), p0); -// } -// -// internal static string MiddewareFilter_NoConfigureMethod -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (MiddewareFilter_NoConfigureMethod), (string) null); -// } -// } -// -// internal static string FormatMiddewareFilter_NoConfigureMethod(object p0, object p1) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("MiddewareFilter_NoConfigureMethod", (string) null), p0, p1); -// } -// -// internal static string MiddlewareFilterBuilder_NoMiddlewareFeature -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (MiddlewareFilterBuilder_NoMiddlewareFeature), (string) null); -// } -// } -// -// internal static string FormatMiddlewareFilterBuilder_NoMiddlewareFeature(object p0) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("MiddlewareFilterBuilder_NoMiddlewareFeature", (string) null), p0); -// } -// -// internal static string MiddlewareFilterBuilder_NullApplicationBuilder -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (MiddlewareFilterBuilder_NullApplicationBuilder), (string) null); -// } -// } -// -// internal static string FormatMiddlewareFilterBuilder_NullApplicationBuilder(object p0) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("MiddlewareFilterBuilder_NullApplicationBuilder", (string) null), p0); -// } -// -// internal static string MiddlewareFilter_InvalidConfigureReturnType -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (MiddlewareFilter_InvalidConfigureReturnType), (string) null); -// } -// } -// -// internal static string FormatMiddlewareFilter_InvalidConfigureReturnType( -// object p0, -// object p1, -// object p2) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("MiddlewareFilter_InvalidConfigureReturnType", (string) null), p0, p1, p2); -// } -// -// internal static string MiddlewareFilter_ServiceResolutionFail -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (MiddlewareFilter_ServiceResolutionFail), (string) null); -// } -// } -// -// internal static string FormatMiddlewareFilter_ServiceResolutionFail( -// object p0, -// object p1, -// object p2, -// object p3) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("MiddlewareFilter_ServiceResolutionFail", (string) null), p0, p1, p2, p3); -// } -// -// internal static string AuthorizeFilter_AuthorizationPolicyCannotBeCreated -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (AuthorizeFilter_AuthorizationPolicyCannotBeCreated), (string) null); -// } -// } -// -// internal static string FormatAuthorizeFilter_AuthorizationPolicyCannotBeCreated( -// object p0, -// object p1) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("AuthorizeFilter_AuthorizationPolicyCannotBeCreated", (string) null), p0, p1); -// } -// -// internal static string FormCollectionModelBinder_CannotBindToFormCollection -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (FormCollectionModelBinder_CannotBindToFormCollection), (string) null); -// } -// } -// -// internal static string FormatFormCollectionModelBinder_CannotBindToFormCollection( -// object p0, -// object p1, -// object p2) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("FormCollectionModelBinder_CannotBindToFormCollection", (string) null), p0, p1, p2); -// } -// -// internal static string VaryByQueryKeys_Requires_ResponseCachingMiddleware -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (VaryByQueryKeys_Requires_ResponseCachingMiddleware), (string) null); -// } -// } -// -// internal static string FormatVaryByQueryKeys_Requires_ResponseCachingMiddleware(object p0) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("VaryByQueryKeys_Requires_ResponseCachingMiddleware", (string) null), p0); -// } -// -// internal static string CandidateResolver_DifferentCasedReference -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (CandidateResolver_DifferentCasedReference), (string) null); -// } -// } -// -// internal static string FormatCandidateResolver_DifferentCasedReference(object p0) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("CandidateResolver_DifferentCasedReference", (string) null), p0); -// } -// -// internal static string MiddlewareFilterConfigurationProvider_CreateConfigureDelegate_CannotCreateType -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (MiddlewareFilterConfigurationProvider_CreateConfigureDelegate_CannotCreateType), (string) null); -// } -// } -// -// internal static string FormatMiddlewareFilterConfigurationProvider_CreateConfigureDelegate_CannotCreateType( -// object p0, -// object p1) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("MiddlewareFilterConfigurationProvider_CreateConfigureDelegate_CannotCreateType", (string) null), p0, p1); -// } -// -// internal static string Argument_InvalidOffsetLength -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (Argument_InvalidOffsetLength), (string) null); -// } -// } -// -// internal static string FormatArgument_InvalidOffsetLength(object p0, object p1) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("Argument_InvalidOffsetLength", (string) null), p0, p1); -// } -// -// internal static string ComplexTypeModelBinder_NoParameterlessConstructor_ForType -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (ComplexTypeModelBinder_NoParameterlessConstructor_ForType), (string) null); -// } -// } -// -// internal static string FormatComplexTypeModelBinder_NoParameterlessConstructor_ForType(object p0) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ComplexTypeModelBinder_NoParameterlessConstructor_ForType", (string) null), p0); -// } -// -// internal static string ComplexTypeModelBinder_NoParameterlessConstructor_ForProperty -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (ComplexTypeModelBinder_NoParameterlessConstructor_ForProperty), (string) null); -// } -// } -// -// internal static string FormatComplexTypeModelBinder_NoParameterlessConstructor_ForProperty( -// object p0, -// object p1, -// object p2) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ComplexTypeModelBinder_NoParameterlessConstructor_ForProperty", (string) null), p0, p1, p2); -// } -// -// internal static string NoRoutesMatchedForPage -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (NoRoutesMatchedForPage), (string) null); -// } -// } -// -// internal static string FormatNoRoutesMatchedForPage(object p0) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("NoRoutesMatchedForPage", (string) null), p0); -// } -// -// internal static string UrlHelper_RelativePagePathIsNotSupported -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (UrlHelper_RelativePagePathIsNotSupported), (string) null); -// } -// } -// -// internal static string FormatUrlHelper_RelativePagePathIsNotSupported( -// object p0, -// object p1, -// object p2) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("UrlHelper_RelativePagePathIsNotSupported", (string) null), p0, p1, p2); -// } -// -// internal static string ValidationProblemDescription_Title -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (ValidationProblemDescription_Title), (string) null); -// } -// } -// -// internal static string ApiController_AttributeRouteRequired -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (ApiController_AttributeRouteRequired), (string) null); -// } -// } -// -// internal static string FormatApiController_AttributeRouteRequired(object p0, object p1) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ApiController_AttributeRouteRequired", (string) null), p0, p1); -// } -// -// internal static string VirtualFileResultExecutor_NoFileProviderConfigured -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (VirtualFileResultExecutor_NoFileProviderConfigured), (string) null); -// } -// } -// -// internal static string ApplicationPartFactory_InvalidFactoryType -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (ApplicationPartFactory_InvalidFactoryType), (string) null); -// } -// } -// -// internal static string FormatApplicationPartFactory_InvalidFactoryType( -// object p0, -// object p1, -// object p2) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ApplicationPartFactory_InvalidFactoryType", (string) null), p0, p1, p2); -// } -// -// internal static string RelatedAssemblyAttribute_AssemblyCannotReferenceSelf -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (RelatedAssemblyAttribute_AssemblyCannotReferenceSelf), (string) null); -// } -// } -// -// internal static string FormatRelatedAssemblyAttribute_AssemblyCannotReferenceSelf( -// object p0, -// object p1) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("RelatedAssemblyAttribute_AssemblyCannotReferenceSelf", (string) null), p0, p1); -// } -// -// internal static string RelatedAssemblyAttribute_CouldNotBeFound -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (RelatedAssemblyAttribute_CouldNotBeFound), (string) null); -// } -// } -// -// internal static string FormatRelatedAssemblyAttribute_CouldNotBeFound( -// object p0, -// object p1, -// object p2) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("RelatedAssemblyAttribute_CouldNotBeFound", (string) null), p0, p1, p2); -// } -// -// internal static string ApplicationAssembliesProvider_DuplicateRelatedAssembly -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (ApplicationAssembliesProvider_DuplicateRelatedAssembly), (string) null); -// } -// } -// -// internal static string FormatApplicationAssembliesProvider_DuplicateRelatedAssembly(object p0) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ApplicationAssembliesProvider_DuplicateRelatedAssembly", (string) null), p0); -// } -// -// internal static string ApplicationAssembliesProvider_RelatedAssemblyCannotDefineAdditional -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (ApplicationAssembliesProvider_RelatedAssemblyCannotDefineAdditional), (string) null); -// } -// } -// -// internal static string FormatApplicationAssembliesProvider_RelatedAssemblyCannotDefineAdditional( -// object p0, -// object p1) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ApplicationAssembliesProvider_RelatedAssemblyCannotDefineAdditional", (string) null), p0, p1); -// } -// -// internal static string ComplexTypeModelBinder_NoParameterlessConstructor_ForParameter -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (ComplexTypeModelBinder_NoParameterlessConstructor_ForParameter), (string) null); -// } -// } -// -// internal static string FormatComplexTypeModelBinder_NoParameterlessConstructor_ForParameter( -// object p0, -// object p1) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ComplexTypeModelBinder_NoParameterlessConstructor_ForParameter", (string) null), p0, p1); -// } -// -// internal static string ApiController_MultipleBodyParametersFound -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (ApiController_MultipleBodyParametersFound), (string) null); -// } -// } -// -// internal static string FormatApiController_MultipleBodyParametersFound( -// object p0, -// object p1, -// object p2, -// object p3) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ApiController_MultipleBodyParametersFound", (string) null), p0, p1, p2, p3); -// } -// -// internal static string ApiConventionMustBeStatic -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (ApiConventionMustBeStatic), (string) null); -// } -// } -// -// internal static string FormatApiConventionMustBeStatic(object p0) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ApiConventionMustBeStatic", (string) null), p0); -// } -// -// internal static string InvalidTypeTForActionResultOfT -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (InvalidTypeTForActionResultOfT), (string) null); -// } -// } -// -// internal static string FormatInvalidTypeTForActionResultOfT(object p0, object p1) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("InvalidTypeTForActionResultOfT", (string) null), p0, p1); -// } -// -// internal static string ApiConvention_UnsupportedAttributesOnConvention -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (ApiConvention_UnsupportedAttributesOnConvention), (string) null); -// } -// } -// -// internal static string FormatApiConvention_UnsupportedAttributesOnConvention( -// object p0, -// object p1, -// object p2) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ApiConvention_UnsupportedAttributesOnConvention", (string) null), p0, p1, p2); -// } -// -// internal static string ApiConventionMethod_AmbiguousMethodName -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (ApiConventionMethod_AmbiguousMethodName), (string) null); -// } -// } -// -// internal static string FormatApiConventionMethod_AmbiguousMethodName(object p0, object p1) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ApiConventionMethod_AmbiguousMethodName", (string) null), p0, p1); -// } -// -// internal static string ApiConventionMethod_NoMethodFound -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (ApiConventionMethod_NoMethodFound), (string) null); -// } -// } -// -// internal static string FormatApiConventionMethod_NoMethodFound(object p0, object p1) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ApiConventionMethod_NoMethodFound", (string) null), p0, p1); -// } -// -// internal static string ValidationVisitor_ExceededMaxDepth -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (ValidationVisitor_ExceededMaxDepth), (string) null); -// } -// } -// -// internal static string FormatValidationVisitor_ExceededMaxDepth( -// object p0, -// object p1, -// object p2) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ValidationVisitor_ExceededMaxDepth", (string) null), p0, p1, p2); -// } -// -// internal static string ValidationVisitor_ExceededMaxDepthFix -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (ValidationVisitor_ExceededMaxDepthFix), (string) null); -// } -// } -// -// internal static string FormatValidationVisitor_ExceededMaxDepthFix(object p0, object p1) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ValidationVisitor_ExceededMaxDepthFix", (string) null), p0, p1); -// } -// -// internal static string ValidationVisitor_ExceededMaxPropertyDepth -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (ValidationVisitor_ExceededMaxPropertyDepth), (string) null); -// } -// } -// -// internal static string FormatValidationVisitor_ExceededMaxPropertyDepth( -// object p0, -// object p1, -// object p2, -// object p3) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ValidationVisitor_ExceededMaxPropertyDepth", (string) null), p0, p1, p2, p3); -// } -// -// internal static string ApiConventions_Title_400 -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (ApiConventions_Title_400), (string) null); -// } -// } -// -// internal static string ApiConventions_Title_401 -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (ApiConventions_Title_401), (string) null); -// } -// } -// -// internal static string ApiConventions_Title_403 -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (ApiConventions_Title_403), (string) null); -// } -// } -// -// internal static string ApiConventions_Title_404 -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (ApiConventions_Title_404), (string) null); -// } -// } -// -// internal static string ApiConventions_Title_406 -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (ApiConventions_Title_406), (string) null); -// } -// } -// -// internal static string ApiConventions_Title_409 -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (ApiConventions_Title_409), (string) null); -// } -// } -// -// internal static string ApiConventions_Title_415 -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (ApiConventions_Title_415), (string) null); -// } -// } -// -// internal static string ApiConventions_Title_422 -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (ApiConventions_Title_422), (string) null); -// } -// } -// -// internal static string ReferenceToNewtonsoftJsonRequired -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (ReferenceToNewtonsoftJsonRequired), (string) null); -// } -// } -// -// internal static string FormatReferenceToNewtonsoftJsonRequired( -// object p0, -// object p1, -// object p2, -// object p3, -// object p4) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ReferenceToNewtonsoftJsonRequired", (string) null), p0, p1, p2, p3, p4); -// } -// -// internal static string ModelBinding_ExceededMaxModelBindingCollectionSize -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (ModelBinding_ExceededMaxModelBindingCollectionSize), (string) null); -// } -// } -// -// internal static string FormatModelBinding_ExceededMaxModelBindingCollectionSize( -// object p0, -// object p1, -// object p2, -// object p3, -// object p4) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ModelBinding_ExceededMaxModelBindingCollectionSize", (string) null), p0, p1, p2, p3, p4); -// } -// -// internal static string ModelBinding_ExceededMaxModelBindingRecursionDepth -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (ModelBinding_ExceededMaxModelBindingRecursionDepth), (string) null); -// } -// } -// -// internal static string FormatModelBinding_ExceededMaxModelBindingRecursionDepth( -// object p0, -// object p1, -// object p2, -// object p3) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ModelBinding_ExceededMaxModelBindingRecursionDepth", (string) null), p0, p1, p2, p3); -// } -// -// internal static string Property_MustBeInstanceOfType -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (Property_MustBeInstanceOfType), (string) null); -// } -// } -// -// internal static string FormatProperty_MustBeInstanceOfType(object p0, object p1, object p2) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("Property_MustBeInstanceOfType", (string) null), p0, p1, p2); -// } -// -// internal static string ObjectResultExecutor_MaxEnumerationExceeded -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (ObjectResultExecutor_MaxEnumerationExceeded), (string) null); -// } -// } -// -// internal static string FormatObjectResultExecutor_MaxEnumerationExceeded(object p0, object p1) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("ObjectResultExecutor_MaxEnumerationExceeded", (string) null), p0, p1); -// } -// -// internal static string UnexpectedJsonEnd -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (UnexpectedJsonEnd), (string) null); -// } -// } -// -// internal static string ApiConventions_Title_500 -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (ApiConventions_Title_500), (string) null); -// } -// } -// -// internal static string FailedToReadRequestForm -// { -// get -// { -// return ResourcesInternal.GetResourceString(nameof (FailedToReadRequestForm), (string) null); -// } -// } -// -// internal static string FormatFailedToReadRequestForm(object p0) -// { -// return string.Format((IFormatProvider) ResourcesInternal.Culture, ResourcesInternal.GetResourceString("FailedToReadRequestForm", (string) null), p0); -// } -// } -// } diff --git a/src/JsonApiDotNetCore/Configuration/MvcInternals/Strategy.cs b/src/JsonApiDotNetCore/Configuration/MvcInternals/Strategy.cs deleted file mode 100644 index 9eb22ea118..0000000000 --- a/src/JsonApiDotNetCore/Configuration/MvcInternals/Strategy.cs +++ /dev/null @@ -1,249 +0,0 @@ -// // Copyright (c) .NET Foundation. All rights reserved. -// // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -// -// using System; -// using System.Collections; -// using System.Collections.Concurrent; -// using System.Collections.Generic; -// using System.Linq.Expressions; -// using System.Reflection; -// using Microsoft.AspNetCore.Mvc.ModelBinding; -// using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; -// -// namespace JsonApiDotNetCore.Configuration -// { -// internal class JsonApiDefaultComplexObjectValidationStrategy : IValidationStrategy -// { -// private static readonly bool IsMono = Type.GetType("Mono.Runtime") != null; -// -// /// -// /// Gets an instance of . -// /// -// public static readonly IValidationStrategy Instance = new JsonApiDefaultComplexObjectValidationStrategy(); -// -// private JsonApiDefaultComplexObjectValidationStrategy() -// { -// } -// -// /// -// public IEnumerator GetChildren( -// ModelMetadata metadata, -// string key, -// object model) -// { -// return new Enumerator(metadata.Properties, key, model); -// } -// -// private class Enumerator : IEnumerator -// { -// private readonly string _key; -// private readonly object _model; -// private readonly ModelPropertyCollection _properties; -// -// private ValidationEntry _entry; -// private int _index; -// -// public Enumerator( -// ModelPropertyCollection properties, -// string key, -// object model) -// { -// _properties = properties; -// _key = key; -// _model = model; -// -// _index = -1; -// } -// -// public ValidationEntry Current => _entry; -// -// object IEnumerator.Current => Current; -// -// public bool MoveNext() -// { -// _index++; -// if (_index >= _properties.Count) -// { -// return false; -// } -// -// var property = _properties[_index]; -// var propertyName = property.BinderModelName ?? property.PropertyName; -// var key = ModelNames.CreatePropertyModelName(_key, propertyName); -// -// if (_model == null) -// { -// // Performance: Never create a delegate when container is null. -// _entry = new ValidationEntry(property, key, model: null); -// } -// else if (IsMono) -// { -// _entry = new ValidationEntry(property, key, () => GetModelOnMono(_model, property.PropertyName)); -// } -// else -// { -// _entry = new ValidationEntry(property, key, () => GetModel(_model, property)); -// } -// -// return true; -// } -// -// public void Dispose() -// { -// } -// -// public void Reset() -// { -// throw new NotImplementedException(); -// } -// -// private static object GetModel(object container, ModelMetadata property) -// { -// return property.PropertyGetter(container); -// } -// -// // Our property accessors don't work on Mono 4.0.4 - see https://github.com/aspnet/External/issues/44 -// // This is a workaround for what the PropertyGetter does in the background. -// private static object GetModelOnMono(object container, string propertyName) -// { -// var propertyInfo = container.GetType().GetRuntimeProperty(propertyName); -// try -// { -// return propertyInfo.GetValue(container); -// } -// catch (TargetInvocationException ex) -// { -// throw ex.InnerException; -// } -// } -// } -// } -// -// -// /// -// /// The default implementation of for a collection. -// /// -// /// -// /// This implementation handles cases like: -// /// -// /// Model: IList<Student> -// /// Query String: ?students[0].Age=8&students[1].Age=9 -// /// -// /// In this case the elements of the collection are identified in the input data set by an incrementing -// /// integer index. -// /// -// /// -// /// or: -// /// -// /// -// /// Model: IDictionary<string, int> -// /// Query String: ?students[0].Key=Joey&students[0].Value=8 -// /// -// /// In this case the dictionary is treated as a collection of key-value pairs, and the elements of the -// /// collection are identified in the input data set by an incrementing integer index. -// /// -// /// -// /// Using this key format, the enumerator enumerates model objects of type matching -// /// . The indices of the elements in the collection are used to -// /// compute the model prefix keys. -// /// -// internal class JsonApiDefaultCollectionValidationStrategy : IValidationStrategy -// { -// private static readonly MethodInfo _getEnumerator = typeof(JsonApiDefaultCollectionValidationStrategy) -// .GetMethod(nameof(GetEnumerator), BindingFlags.Static | BindingFlags.NonPublic); -// -// /// -// /// Gets an instance of . -// /// -// public static readonly JsonApiDefaultCollectionValidationStrategy Instance = new JsonApiDefaultCollectionValidationStrategy(); -// private readonly ConcurrentDictionary> _genericGetEnumeratorCache = new ConcurrentDictionary>(); -// -// private JsonApiDefaultCollectionValidationStrategy() -// { -// } -// -// /// -// public IEnumerator GetChildren( -// ModelMetadata metadata, -// string key, -// object model) -// { -// var enumerator = GetEnumeratorForElementType(metadata, model); -// return new Enumerator(metadata.ElementMetadata, key, enumerator); -// } -// -// public IEnumerator GetEnumeratorForElementType(ModelMetadata metadata, object model) -// { -// Func getEnumerator = _genericGetEnumeratorCache.GetOrAdd( -// key: metadata.ElementType, -// valueFactory: (type) => { -// var getEnumeratorMethod = _getEnumerator.MakeGenericMethod(type); -// var parameter = Expression.Parameter(typeof(object), "model"); -// var expression = -// Expression.Lambda>( -// Expression.Call(null, getEnumeratorMethod, parameter), -// parameter); -// return expression.Compile(); -// }); -// -// return getEnumerator(model); -// } -// -// // Called via reflection. -// private static IEnumerator GetEnumerator(object model) -// { -// return (model as IEnumerable)?.GetEnumerator() ?? ((IEnumerable)model).GetEnumerator(); -// } -// -// private class Enumerator : IEnumerator -// { -// private readonly string _key; -// private readonly ModelMetadata _metadata; -// private readonly IEnumerator _enumerator; -// -// private ValidationEntry _entry; -// private int _index; -// -// public Enumerator( -// ModelMetadata metadata, -// string key, -// IEnumerator enumerator) -// { -// _metadata = metadata; -// _key = key; -// _enumerator = enumerator; -// -// _index = -1; -// } -// -// public ValidationEntry Current => _entry; -// -// object IEnumerator.Current => Current; -// -// public bool MoveNext() -// { -// _index++; -// if (!_enumerator.MoveNext()) -// { -// return false; -// } -// -// var key = ModelNames.CreateIndexModelName(_key, _index); -// var model = _enumerator.Current; -// -// _entry = new ValidationEntry(_metadata, key, model); -// -// return true; -// } -// -// public void Dispose() -// { -// } -// -// public void Reset() -// { -// _enumerator.Reset(); -// } -// } -// } -// } diff --git a/src/JsonApiDotNetCore/Configuration/MvcInternals/JsonApiMetaDataProvider.cs b/src/JsonApiDotNetCore/Configuration/Validation/JsonApiMetaDataProvider.cs similarity index 80% rename from src/JsonApiDotNetCore/Configuration/MvcInternals/JsonApiMetaDataProvider.cs rename to src/JsonApiDotNetCore/Configuration/Validation/JsonApiMetaDataProvider.cs index 10a0aee3c0..bd84358f01 100644 --- a/src/JsonApiDotNetCore/Configuration/MvcInternals/JsonApiMetaDataProvider.cs +++ b/src/JsonApiDotNetCore/Configuration/Validation/JsonApiMetaDataProvider.cs @@ -1,17 +1,14 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata; using Microsoft.Extensions.Options; -namespace JsonApiDotNetCore.Configuration +namespace JsonApiDotNetCore.Configuration.Validation { /// /// A default implementation of based on reflection. /// - public class JsonApiModelMetadataProvider : DefaultModelMetadataProvider + internal class JsonApiModelMetadataProvider : DefaultModelMetadataProvider { public JsonApiModelMetadataProvider(ICompositeMetadataDetailsProvider detailsProvider) : base(detailsProvider) { } diff --git a/src/JsonApiDotNetCore/Configuration/MvcInternals/JsonApiObjectValidator.cs b/src/JsonApiDotNetCore/Configuration/Validation/JsonApiObjectValidator.cs similarity index 95% rename from src/JsonApiDotNetCore/Configuration/MvcInternals/JsonApiObjectValidator.cs rename to src/JsonApiDotNetCore/Configuration/Validation/JsonApiObjectValidator.cs index 92596c1073..7d21ed640c 100644 --- a/src/JsonApiDotNetCore/Configuration/MvcInternals/JsonApiObjectValidator.cs +++ b/src/JsonApiDotNetCore/Configuration/Validation/JsonApiObjectValidator.cs @@ -1,13 +1,10 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - using System; using System.Collections.Generic; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; -namespace JsonApiDotNetCore.Configuration +namespace JsonApiDotNetCore.Configuration.Validation { /// /// The default implementation of . diff --git a/src/JsonApiDotNetCore/Configuration/Validation/JsonApiValidationVisitor.cs b/src/JsonApiDotNetCore/Configuration/Validation/JsonApiValidationVisitor.cs new file mode 100644 index 0000000000..a0380ad7c4 --- /dev/null +++ b/src/JsonApiDotNetCore/Configuration/Validation/JsonApiValidationVisitor.cs @@ -0,0 +1,45 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.ModelBinding; +using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; + +namespace JsonApiDotNetCore.Configuration.Validation +{ + internal sealed class JsonApiValidationVisitor : ValidationVisitor + { + public JsonApiValidationVisitor( + ActionContext actionContext, + IModelValidatorProvider validatorProvider, + ValidatorCache validatorCache, + IModelMetadataProvider metadataProvider, + ValidationStateDictionary validationState) + : base(actionContext, validatorProvider, validatorCache, metadataProvider, validationState) { } + + protected override bool VisitChildren(IValidationStrategy strategy) + { + var isValid = true; + var enumerator = strategy.GetChildren(Metadata, Key, Model); + var parentEntry = new ValidationEntry(Metadata, Key, Model); + + while (enumerator.MoveNext()) + { + var entry = enumerator.Current; + var metadata = entry.Metadata; + var key = entry.Key; + + var jsonApiFilter = metadata.PropertyValidationFilter as PartialPatchValidationFilter; + var serviceProvider = Context?.HttpContext?.RequestServices; + + if (metadata.PropertyValidationFilter?.ShouldValidateEntry(entry, parentEntry) == false + || jsonApiFilter != null && jsonApiFilter.ShouldValidateEntry(entry, parentEntry, serviceProvider) == false ) + { + SuppressValidation(key); + continue; + } + + isValid &= Visit(metadata, key, entry.Model); + } + + return isValid; + } + } +} diff --git a/src/JsonApiDotNetCore/Configuration/MvcInternals/JsonApiValidationFilter.cs b/src/JsonApiDotNetCore/Configuration/Validation/PartialPatchValidationFilter.cs similarity index 94% rename from src/JsonApiDotNetCore/Configuration/MvcInternals/JsonApiValidationFilter.cs rename to src/JsonApiDotNetCore/Configuration/Validation/PartialPatchValidationFilter.cs index fd90db90da..6a414cb344 100644 --- a/src/JsonApiDotNetCore/Configuration/MvcInternals/JsonApiValidationFilter.cs +++ b/src/JsonApiDotNetCore/Configuration/Validation/PartialPatchValidationFilter.cs @@ -5,15 +5,14 @@ using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; using Microsoft.Extensions.DependencyInjection; -namespace JsonApiDotNetCore.Configuration +namespace JsonApiDotNetCore.Configuration.Validation { internal sealed class PartialPatchValidationFilter : IPropertyValidationFilter { /// public bool ShouldValidateEntry(ValidationEntry entry, ValidationEntry parentEntry) => true; - public bool ShouldValidateEntry(ValidationEntry entry, ValidationEntry parentEntry, - IServiceProvider serviceProvider) + public bool ShouldValidateEntry(ValidationEntry entry, ValidationEntry parentEntry, IServiceProvider serviceProvider) { if (serviceProvider == null) throw new ArgumentException(nameof(serviceProvider)); diff --git a/src/JsonApiDotNetCore/Resources/Annotations/JsonApiRequiredAttribute.cs b/src/JsonApiDotNetCore/Resources/Annotations/JsonApiRequiredAttribute.cs deleted file mode 100644 index ca88b2a3cd..0000000000 --- a/src/JsonApiDotNetCore/Resources/Annotations/JsonApiRequiredAttribute.cs +++ /dev/null @@ -1,118 +0,0 @@ -using System; -using System.Collections; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using JsonApiDotNetCore.Configuration; -using JsonApiDotNetCore.Middleware; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc.ModelBinding; -using Microsoft.Extensions.DependencyInjection; - -namespace JsonApiDotNetCore.Resources.Annotations -{ - /// - /// Used with model state validation as a replacement for the built-in to support partial updates. - /// - internal sealed class JsonApiRequiredAttribute : RequiredAttribute - { - private const string _isSelfReferencingResourceKey = "JsonApiDotNetCore_IsSelfReferencingResource"; - - public override bool RequiresValidationContext => true; - - /// - protected override ValidationResult IsValid(object value, ValidationContext validationContext) - { - if (validationContext == null) throw new ArgumentNullException(nameof(validationContext)); - - var request = validationContext.GetRequiredService(); - var httpContextAccessor = validationContext.GetRequiredService(); - - if (ShouldSkipValidationForResource(validationContext, request, httpContextAccessor.HttpContext) || - ShouldSkipValidationForProperty(validationContext, httpContextAccessor.HttpContext)) - { - return ValidationResult.Success; - } - - return base.IsValid(value, validationContext); - } - - private bool ShouldSkipValidationForResource(ValidationContext validationContext, IJsonApiRequest request, - HttpContext httpContext) - { - if (request.Kind == EndpointKind.Primary) - { - // If there is a relationship included in the data of the POST or PATCH, then the 'IsRequired' attribute will be disabled for any - // property within that object. For instance, a new article is posted and has a relationship included to an author. In this case, - // the author name (which has the 'IsRequired' attribute) will not be included in the POST. Unless disabled, the POST will fail. - - if (validationContext.ObjectType != request.PrimaryResource.ResourceType) - { - return true; - } - - if (validationContext.ObjectInstance is IIdentifiable identifiable) - { - if (identifiable.StringId != request.PrimaryId) - { - return true; - } - - var isSelfReferencingResource = (bool?) httpContext.Items[_isSelfReferencingResourceKey]; - - if (isSelfReferencingResource == null) - { - // When processing a request, the first time we get here is for the top-level resource. - // Subsequent validations for related resources inspect the cache to know that their validation can be skipped. - - isSelfReferencingResource = IsSelfReferencingResource(identifiable, validationContext); - httpContext.Items[_isSelfReferencingResourceKey] = isSelfReferencingResource; - } - - if (isSelfReferencingResource.Value) - { - return true; - } - } - } - - return false; - } - - private bool IsSelfReferencingResource(IIdentifiable identifiable, ValidationContext validationContext) - { - var provider = validationContext.GetRequiredService(); - var relationships = provider.GetResourceContext(validationContext.ObjectType).Relationships; - - foreach (var relationship in relationships) - { - if (relationship is HasOneAttribute hasOne) - { - var relationshipValue = (IIdentifiable) hasOne.GetValue(identifiable); - if (IdentifiableComparer.Instance.Equals(identifiable, relationshipValue)) - { - return true; - } - } - - if (relationship is HasManyAttribute hasMany) - { - var collection = (IEnumerable) hasMany.GetValue(identifiable); - - if (collection != null && collection.OfType().Any(resource => - IdentifiableComparer.Instance.Equals(identifiable, resource))) - { - return true; - } - } - } - - return false; - } - - private bool ShouldSkipValidationForProperty(ValidationContext validationContext, HttpContext httpContext) - { - return httpContext.IsRequiredValidatorDisabled(validationContext.MemberName, - validationContext.ObjectType.Name); - } - } -} From 5cce5ba493ad322cced875b9cd63beeff10832d6 Mon Sep 17 00:00:00 2001 From: maurei Date: Sat, 3 Oct 2020 13:38:44 +0200 Subject: [PATCH 17/27] fix: self review --- .../Models/Article.cs | 1 - .../JsonApiDotNetCoreExample/Models/Author.cs | 1 - .../ApplicationBuilderExtensions.cs | 4 - .../Configuration/CustomProvider.cs | 178 ------------------ .../Configuration/CustomValidator.cs | 143 -------------- .../JsonApiApplicationBuilder.cs | 2 +- .../Middleware/HttpContextExtensions.cs | 10 +- .../Serialization/JsonApiReader.cs | 7 +- 8 files changed, 8 insertions(+), 338 deletions(-) delete mode 100644 src/JsonApiDotNetCore/Configuration/CustomProvider.cs delete mode 100644 src/JsonApiDotNetCore/Configuration/CustomValidator.cs diff --git a/src/Examples/JsonApiDotNetCoreExample/Models/Article.cs b/src/Examples/JsonApiDotNetCoreExample/Models/Article.cs index 49ddc41095..455ed51039 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Models/Article.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Models/Article.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using JsonApiDotNetCore.Resources; using JsonApiDotNetCore.Resources.Annotations; diff --git a/src/Examples/JsonApiDotNetCoreExample/Models/Author.cs b/src/Examples/JsonApiDotNetCoreExample/Models/Author.cs index f194542043..7280263468 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Models/Author.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Models/Author.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; using JsonApiDotNetCore.Resources; using JsonApiDotNetCore.Resources.Annotations; diff --git a/src/JsonApiDotNetCore/Configuration/ApplicationBuilderExtensions.cs b/src/JsonApiDotNetCore/Configuration/ApplicationBuilderExtensions.cs index e7e31ea870..ea3970ab83 100644 --- a/src/JsonApiDotNetCore/Configuration/ApplicationBuilderExtensions.cs +++ b/src/JsonApiDotNetCore/Configuration/ApplicationBuilderExtensions.cs @@ -1,11 +1,7 @@ using System; using JsonApiDotNetCore.Middleware; using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Mvc.DataAnnotations; -using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Localization; -using Microsoft.Extensions.Options; namespace JsonApiDotNetCore.Configuration { diff --git a/src/JsonApiDotNetCore/Configuration/CustomProvider.cs b/src/JsonApiDotNetCore/Configuration/CustomProvider.cs deleted file mode 100644 index 053ad097ee..0000000000 --- a/src/JsonApiDotNetCore/Configuration/CustomProvider.cs +++ /dev/null @@ -1,178 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using Microsoft.AspNetCore.Mvc.DataAnnotations; -using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; -using Microsoft.Extensions.Localization; -using Microsoft.Extensions.Options; - -namespace JsonApiDotNetCore.Configuration -{ - /// - /// An implementation of which provides validators - /// for attributes which derive from . It also provides - /// a validator for types which implement . - /// - internal sealed class DataAnnotationsModelValidatorProvider_COPY : IMetadataBasedModelValidatorProvider - { - private readonly IOptions _options; - private readonly IStringLocalizerFactory _stringLocalizerFactory; - private readonly IValidationAttributeAdapterProvider _validationAttributeAdapterProvider; - - /// - /// Create a new instance of . - /// - /// The - /// that supplies s. - /// The . - /// The . - /// and - /// are nullable only for testing ease. - public DataAnnotationsModelValidatorProvider_COPY( - IValidationAttributeAdapterProvider validationAttributeAdapterProvider, - IOptions options, - IStringLocalizerFactory stringLocalizerFactory) - { - if (validationAttributeAdapterProvider == null) - { - throw new ArgumentNullException(nameof(validationAttributeAdapterProvider)); - } - if (options == null) - { - throw new ArgumentNullException(nameof(options)); - } - - _validationAttributeAdapterProvider = validationAttributeAdapterProvider; - _options = options; - _stringLocalizerFactory = stringLocalizerFactory; - } - - public void CreateValidators(ModelValidatorProviderContext context) - { - IStringLocalizer stringLocalizer = null; - if (_stringLocalizerFactory != null && _options.Value.DataAnnotationLocalizerProvider != null) - { - stringLocalizer = _options.Value.DataAnnotationLocalizerProvider( - context.ModelMetadata.ContainerType ?? context.ModelMetadata.ModelType, - _stringLocalizerFactory); - } - - for (var i = 0; i < context.Results.Count; i++) - { - var validatorItem = context.Results[i]; - if (validatorItem.Validator != null) - { - continue; - } - - if (!(validatorItem.ValidatorMetadata is ValidationAttribute attribute)) - { - continue; - } - - var validator = new DataAnnotationsModelValidator_COPY( - _validationAttributeAdapterProvider, - attribute, - stringLocalizer); - - validatorItem.Validator = validator; - validatorItem.IsReusable = true; - // Inserts validators based on whether or not they are 'required'. We want to run - // 'required' validators first so that we get the best possible error message. - if (attribute is RequiredAttribute) - { - context.Results.Remove(validatorItem); - context.Results.Insert(0, validatorItem); - } - } - - // Produce a validator if the type supports IValidatableObject - if (typeof(IValidatableObject).IsAssignableFrom(context.ModelMetadata.ModelType)) - { - context.Results.Add(new ValidatorItem - { - Validator = new ValidatableObjectAdapter_COPY(), - IsReusable = true - }); - } - } - - public bool HasValidators(Type modelType, IList validatorMetadata) - { - if (typeof(IValidatableObject).IsAssignableFrom(modelType)) - { - return true; - } - - for (var i = 0; i < validatorMetadata.Count; i++) - { - if (validatorMetadata[i] is ValidationAttribute) - { - return true; - } - } - - return false; - } - } - - public class ValidatableObjectAdapter_COPY : IModelValidator - { - public IEnumerable Validate(ModelValidationContext context) - { - var model = context.Model; - if (model == null) - { - return Enumerable.Empty(); - } - - if (!(model is IValidatableObject validatable)) - { - var message = "null"; - - throw new InvalidOperationException(message); - } - - // The constructed ValidationContext is intentionally slightly different from what - // DataAnnotationsModelValidator creates. The instance parameter would be context.Container - // (if non-null) in that class. But, DataAnnotationsModelValidator _also_ passes context.Model - // separately to any ValidationAttribute. - var validationContext = new ValidationContext( - instance: validatable, - serviceProvider: context.ActionContext?.HttpContext?.RequestServices, - items: null) - { - DisplayName = context.ModelMetadata.GetDisplayName(), - MemberName = context.ModelMetadata.Name, - }; - - return ConvertResults(validatable.Validate(validationContext)); - } - - private IEnumerable ConvertResults(IEnumerable results) - { - foreach (var result in results) - { - if (result != ValidationResult.Success) - { - if (result.MemberNames == null || !result.MemberNames.Any()) - { - yield return new ModelValidationResult(memberName: null, message: result.ErrorMessage); - } - else - { - foreach (var memberName in result.MemberNames) - { - yield return new ModelValidationResult(memberName, result.ErrorMessage); - } - } - } - } - } - } - -} diff --git a/src/JsonApiDotNetCore/Configuration/CustomValidator.cs b/src/JsonApiDotNetCore/Configuration/CustomValidator.cs deleted file mode 100644 index 840d9d72aa..0000000000 --- a/src/JsonApiDotNetCore/Configuration/CustomValidator.cs +++ /dev/null @@ -1,143 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using Microsoft.AspNetCore.Mvc.DataAnnotations; -using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; -using Microsoft.Extensions.Localization; - -namespace JsonApiDotNetCore.Configuration -{ - /// - /// Validates based on the given . - /// - internal class DataAnnotationsModelValidator_COPY : IModelValidator - { - private static readonly object _emptyValidationContextInstance = new object(); - private readonly IStringLocalizer _stringLocalizer; - private readonly IValidationAttributeAdapterProvider _validationAttributeAdapterProvider; - - /// - /// Create a new instance of . - /// - /// The that defines what we're validating. - /// The used to create messages. - /// The - /// which 's will be created from. - public DataAnnotationsModelValidator_COPY( - IValidationAttributeAdapterProvider validationAttributeAdapterProvider, - ValidationAttribute attribute, - IStringLocalizer stringLocalizer) - { - if (validationAttributeAdapterProvider == null) - { - throw new ArgumentNullException(nameof(validationAttributeAdapterProvider)); - } - - if (attribute == null) - { - throw new ArgumentNullException(nameof(attribute)); - } - - _validationAttributeAdapterProvider = validationAttributeAdapterProvider; - Attribute = attribute; - _stringLocalizer = stringLocalizer; - } - - /// - /// The attribute being validated against. - /// - public ValidationAttribute Attribute { get; } - - /// - /// Validates the context against the . - /// - /// The context being validated. - /// An enumerable of the validation results. - public IEnumerable Validate(ModelValidationContext validationContext) - { - if (validationContext == null) - { - throw new ArgumentNullException(nameof(validationContext)); - } - if (validationContext.ModelMetadata == null) - { - throw new ArgumentException(); - } - if (validationContext.MetadataProvider == null) - { - throw new ArgumentException(); - } - - var metadata = validationContext.ModelMetadata; - var memberName = metadata.Name; - var container = validationContext.Container; - - var context = new ValidationContext( - instance: container ?? validationContext.Model ?? _emptyValidationContextInstance, - serviceProvider: validationContext.ActionContext?.HttpContext?.RequestServices, - items: null) - { - DisplayName = metadata.GetDisplayName(), - MemberName = memberName - }; - - var result = Attribute.GetValidationResult(validationContext.Model, context); - if (result != ValidationResult.Success) - { - string errorMessage; - if (_stringLocalizer != null && - !string.IsNullOrEmpty(Attribute.ErrorMessage) && - string.IsNullOrEmpty(Attribute.ErrorMessageResourceName) && - Attribute.ErrorMessageResourceType == null) - { - errorMessage = GetErrorMessage(validationContext) ?? result.ErrorMessage; - } - else - { - errorMessage = result.ErrorMessage; - } - - var validationResults = new List(); - if (result.MemberNames != null) - { - foreach (var resultMemberName in result.MemberNames) - { - // ModelValidationResult.MemberName is used by invoking validators (such as ModelValidator) to - // append construct the ModelKey for ModelStateDictionary. When validating at type level we - // want the returned MemberNames if specified (e.g. "person.Address.FirstName"). For property - // validation, the ModelKey can be constructed using the ModelMetadata and we should ignore - // MemberName (we don't want "person.Name.Name"). However the invoking validator does not have - // a way to distinguish between these two cases. Consequently we'll only set MemberName if this - // validation returns a MemberName that is different from the property being validated. - var newMemberName = string.Equals(resultMemberName, memberName, StringComparison.Ordinal) ? - null : - resultMemberName; - var validationResult = new ModelValidationResult(newMemberName, errorMessage); - - validationResults.Add(validationResult); - } - } - - if (validationResults.Count == 0) - { - // result.MemberNames was null or empty. - validationResults.Add(new ModelValidationResult(memberName: null, message: errorMessage)); - } - - return validationResults; - } - - return Enumerable.Empty(); - } - - private string GetErrorMessage(ModelValidationContextBase validationContext) - { - var adapter = _validationAttributeAdapterProvider.GetAttributeAdapter(Attribute, _stringLocalizer); - return adapter?.GetErrorMessage(validationContext); - } - } -} diff --git a/src/JsonApiDotNetCore/Configuration/JsonApiApplicationBuilder.cs b/src/JsonApiDotNetCore/Configuration/JsonApiApplicationBuilder.cs index 352c933129..1343586d38 100644 --- a/src/JsonApiDotNetCore/Configuration/JsonApiApplicationBuilder.cs +++ b/src/JsonApiDotNetCore/Configuration/JsonApiApplicationBuilder.cs @@ -133,7 +133,7 @@ public void ConfigureServiceContainer(ICollection dbContextTypes) _services.AddScoped(typeof(IDbContextResolver), contextResolverType); } } - + AddResourceLayer(); AddRepositoryLayer(); AddServiceLayer(); diff --git a/src/JsonApiDotNetCore/Middleware/HttpContextExtensions.cs b/src/JsonApiDotNetCore/Middleware/HttpContextExtensions.cs index 3be172e755..1e11c9758f 100644 --- a/src/JsonApiDotNetCore/Middleware/HttpContextExtensions.cs +++ b/src/JsonApiDotNetCore/Middleware/HttpContextExtensions.cs @@ -18,14 +18,6 @@ public static bool IsJsonApiRequest(this HttpContext httpContext) string value = httpContext.Items[_isJsonApiRequestKey] as string; return value == bool.TrueString; } - - /// - /// Indicates whether the currently executing HTTP request is a PATCH or POST request. - /// - public static bool IsPatchOrPostRequest(this HttpContext httpContext) - { - return httpContext.Request.Method == HttpMethods.Patch || httpContext.Request.Method == HttpMethods.Post; - } internal static void RegisterJsonApiRequest(this HttpContext httpContext) { @@ -43,7 +35,7 @@ internal static void DisableRequiredValidator(this HttpContext httpContext, stri var itemKey = $"{_disableRequiredValidatorKey}_{model}_{propertyName}"; httpContext.Items[itemKey] = true; } - + internal static bool IsRequiredValidatorDisabled(this HttpContext httpContext, string propertyName, string model) { if (httpContext == null) throw new ArgumentNullException(nameof(httpContext)); diff --git a/src/JsonApiDotNetCore/Serialization/JsonApiReader.cs b/src/JsonApiDotNetCore/Serialization/JsonApiReader.cs index e31bd35804..3ec623964d 100644 --- a/src/JsonApiDotNetCore/Serialization/JsonApiReader.cs +++ b/src/JsonApiDotNetCore/Serialization/JsonApiReader.cs @@ -78,7 +78,7 @@ public async Task ReadAsync(InputFormatterContext context) private void ValidateIncomingResourceType(InputFormatterContext context, object model) { - if (context.HttpContext.IsJsonApiRequest() && context.HttpContext.IsPatchOrPostRequest()) + if (context.HttpContext.IsJsonApiRequest() && IsPatchOrPostRequest(context.HttpContext.Request)) { var endpointResourceType = GetEndpointResourceType(); if (endpointResourceType == null) @@ -171,6 +171,11 @@ private async Task GetRequestBody(Stream body) return await reader.ReadToEndAsync(); } + private bool IsPatchOrPostRequest(HttpRequest request) + { + return request.Method == HttpMethods.Patch || request.Method == HttpMethods.Post; + } + private IEnumerable GetBodyResourceTypes(object model) { if (model is IEnumerable resourceCollection) From fda2a3f5241054e3246369827c48dd0a2950f0cb Mon Sep 17 00:00:00 2001 From: maurei Date: Sat, 3 Oct 2020 13:40:06 +0200 Subject: [PATCH 18/27] fix: remove unused extensions --- .../Middleware/HttpContextExtensions.cs | 20 ------------------- .../Serialization/RequestDeserializer.cs | 16 --------------- 2 files changed, 36 deletions(-) diff --git a/src/JsonApiDotNetCore/Middleware/HttpContextExtensions.cs b/src/JsonApiDotNetCore/Middleware/HttpContextExtensions.cs index 1e11c9758f..ccafcae28c 100644 --- a/src/JsonApiDotNetCore/Middleware/HttpContextExtensions.cs +++ b/src/JsonApiDotNetCore/Middleware/HttpContextExtensions.cs @@ -6,7 +6,6 @@ namespace JsonApiDotNetCore.Middleware public static class HttpContextExtensions { private const string _isJsonApiRequestKey = "JsonApiDotNetCore_IsJsonApiRequest"; - private const string _disableRequiredValidatorKey = "JsonApiDotNetCore_DisableRequiredValidator"; /// /// Indicates whether the currently executing HTTP request is being handled by JsonApiDotNetCore. @@ -25,24 +24,5 @@ internal static void RegisterJsonApiRequest(this HttpContext httpContext) httpContext.Items[_isJsonApiRequestKey] = bool.TrueString; } - - internal static void DisableRequiredValidator(this HttpContext httpContext, string propertyName, string model) - { - if (httpContext == null) throw new ArgumentNullException(nameof(httpContext)); - if (propertyName == null) throw new ArgumentNullException(nameof(propertyName)); - if (model == null) throw new ArgumentNullException(nameof(model)); - - var itemKey = $"{_disableRequiredValidatorKey}_{model}_{propertyName}"; - httpContext.Items[itemKey] = true; - } - - internal static bool IsRequiredValidatorDisabled(this HttpContext httpContext, string propertyName, string model) - { - if (httpContext == null) throw new ArgumentNullException(nameof(httpContext)); - if (propertyName == null) throw new ArgumentNullException(nameof(propertyName)); - if (model == null) throw new ArgumentNullException(nameof(model)); - - return httpContext.Items.ContainsKey($"{_disableRequiredValidatorKey}_{model}_{propertyName}"); - } } } diff --git a/src/JsonApiDotNetCore/Serialization/RequestDeserializer.cs b/src/JsonApiDotNetCore/Serialization/RequestDeserializer.cs index e9828500af..d093514da9 100644 --- a/src/JsonApiDotNetCore/Serialization/RequestDeserializer.cs +++ b/src/JsonApiDotNetCore/Serialization/RequestDeserializer.cs @@ -74,22 +74,6 @@ protected override IIdentifiable SetAttributes(IIdentifiable resource, IDictiona if (resource == null) throw new ArgumentNullException(nameof(resource)); if (attributes == null) throw new ArgumentNullException(nameof(attributes)); - if (_httpContextAccessor.HttpContext.Request.Method == HttpMethod.Patch.Method) - { - foreach (AttrAttribute attr in attributes) - { - if (attr.Property.GetCustomAttribute() != null) - { - bool disableValidator = attributeValues == null || !attributeValues.ContainsKey(attr.PublicName); - - if (disableValidator) - { - _httpContextAccessor.HttpContext.DisableRequiredValidator(attr.Property.Name, resource.GetType().Name); - } - } - } - } - return base.SetAttributes(resource, attributeValues, attributes); } } From 1cd1a5cf7e6c7836a4249ff60ff2e34be953c17d Mon Sep 17 00:00:00 2001 From: maurei Date: Sat, 3 Oct 2020 13:40:35 +0200 Subject: [PATCH 19/27] fix --- src/JsonApiDotNetCore/Serialization/RequestDeserializer.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/JsonApiDotNetCore/Serialization/RequestDeserializer.cs b/src/JsonApiDotNetCore/Serialization/RequestDeserializer.cs index d093514da9..acea480c5a 100644 --- a/src/JsonApiDotNetCore/Serialization/RequestDeserializer.cs +++ b/src/JsonApiDotNetCore/Serialization/RequestDeserializer.cs @@ -1,11 +1,8 @@ using System; using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; using System.Net.Http; -using System.Reflection; using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Errors; -using JsonApiDotNetCore.Middleware; using JsonApiDotNetCore.Resources; using JsonApiDotNetCore.Resources.Annotations; using JsonApiDotNetCore.Serialization.Objects; From bb87e15ed5b725137616277742da8cbf6449f28c Mon Sep 17 00:00:00 2001 From: maurei Date: Sat, 3 Oct 2020 13:48:06 +0200 Subject: [PATCH 20/27] fix: docs --- .../Validation/JsonApiMetaDataProvider.cs | 9 ++++-- .../Validation/JsonApiObjectValidator.cs | 31 ++++++------------- .../Validation/JsonApiValidationVisitor.cs | 9 ++++++ .../PartialPatchValidationFilter.cs | 3 ++ 4 files changed, 29 insertions(+), 23 deletions(-) diff --git a/src/JsonApiDotNetCore/Configuration/Validation/JsonApiMetaDataProvider.cs b/src/JsonApiDotNetCore/Configuration/Validation/JsonApiMetaDataProvider.cs index bd84358f01..e38e03ac2e 100644 --- a/src/JsonApiDotNetCore/Configuration/Validation/JsonApiMetaDataProvider.cs +++ b/src/JsonApiDotNetCore/Configuration/Validation/JsonApiMetaDataProvider.cs @@ -1,21 +1,26 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata; +using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; using Microsoft.Extensions.Options; namespace JsonApiDotNetCore.Configuration.Validation { /// - /// A default implementation of based on reflection. + /// Custom implementation of that sets an additional + /// to support partial patching. /// internal class JsonApiModelMetadataProvider : DefaultModelMetadataProvider { + /// public JsonApiModelMetadataProvider(ICompositeMetadataDetailsProvider detailsProvider) : base(detailsProvider) { } - + + /// public JsonApiModelMetadataProvider(ICompositeMetadataDetailsProvider detailsProvider, IOptions optionsAccessor) : base(detailsProvider, optionsAccessor) { } + /// protected override ModelMetadata CreateModelMetadata(DefaultMetadataDetails entry) { var metadata = new DefaultModelMetadata(this, DetailsProvider, entry, ModelBindingMessageProvider) ; diff --git a/src/JsonApiDotNetCore/Configuration/Validation/JsonApiObjectValidator.cs b/src/JsonApiDotNetCore/Configuration/Validation/JsonApiObjectValidator.cs index 7d21ed640c..48bafcc124 100644 --- a/src/JsonApiDotNetCore/Configuration/Validation/JsonApiObjectValidator.cs +++ b/src/JsonApiDotNetCore/Configuration/Validation/JsonApiObjectValidator.cs @@ -7,21 +7,20 @@ namespace JsonApiDotNetCore.Configuration.Validation { /// - /// The default implementation of . + /// Custom implementation of that is identical to DefaultObjectValidator, apart from + /// using our own instead of the built-in . /// + /// + /// See https://github.com/dotnet/aspnetcore/blob/v3.1.8/src/Mvc/Mvc.Core/src/ModelBinding/Validation/DefaultObjectValidator.cs + /// internal class JsonApiObjectValidator : ObjectModelValidator { private readonly IModelMetadataProvider _modelMetadataProvider; private readonly MvcOptions _mvcOptions; private readonly ValidatorCache _validatorCache; private readonly CompositeModelValidatorProvider _validatorProvider; - - /// - /// Initializes a new instance of . - /// - /// The . - /// The list of . - /// Accessor to . + + /// public JsonApiObjectValidator( IModelMetadataProvider modelMetadataProvider, IList validatorProviders, @@ -39,6 +38,7 @@ public JsonApiObjectValidator( _mvcOptions = mvcOptions; } + /// public override ValidationVisitor GetValidationVisitor( ActionContext actionContext, IModelValidatorProvider validatorProvider, @@ -77,19 +77,8 @@ public override void Validate( var metadata = model == null ? null : _modelMetadataProvider.GetMetadataForType(model.GetType()); visitor.Validate(metadata, prefix, model, alwaysValidateAtTopLevel: false); } - - /// - /// Validates the provided object model. - /// If is and the 's - /// is , will add one or more - /// model state errors that - /// would not. - /// - /// The . - /// The . - /// The model prefix key. - /// The model object. - /// The . + + /// public override void Validate( ActionContext actionContext, ValidationStateDictionary validationState, diff --git a/src/JsonApiDotNetCore/Configuration/Validation/JsonApiValidationVisitor.cs b/src/JsonApiDotNetCore/Configuration/Validation/JsonApiValidationVisitor.cs index a0380ad7c4..47984f95bc 100644 --- a/src/JsonApiDotNetCore/Configuration/Validation/JsonApiValidationVisitor.cs +++ b/src/JsonApiDotNetCore/Configuration/Validation/JsonApiValidationVisitor.cs @@ -4,8 +4,16 @@ namespace JsonApiDotNetCore.Configuration.Validation { + /// + /// An extension of the internal that performs an additional check related to + /// property validation filters + /// + /// + /// see **ADD URL TO ASPNETCORE ISSUE RELATED TO THIS** for background information. + /// internal sealed class JsonApiValidationVisitor : ValidationVisitor { + /// public JsonApiValidationVisitor( ActionContext actionContext, IModelValidatorProvider validatorProvider, @@ -14,6 +22,7 @@ public JsonApiValidationVisitor( ValidationStateDictionary validationState) : base(actionContext, validatorProvider, validatorCache, metadataProvider, validationState) { } + /// protected override bool VisitChildren(IValidationStrategy strategy) { var isValid = true; diff --git a/src/JsonApiDotNetCore/Configuration/Validation/PartialPatchValidationFilter.cs b/src/JsonApiDotNetCore/Configuration/Validation/PartialPatchValidationFilter.cs index 6a414cb344..b3be28f17e 100644 --- a/src/JsonApiDotNetCore/Configuration/Validation/PartialPatchValidationFilter.cs +++ b/src/JsonApiDotNetCore/Configuration/Validation/PartialPatchValidationFilter.cs @@ -7,6 +7,9 @@ namespace JsonApiDotNetCore.Configuration.Validation { + /// + /// Validation filter that enables partial patching as part of the json:api spec. + /// internal sealed class PartialPatchValidationFilter : IPropertyValidationFilter { /// From d0f83cfe19164f88848b67d797a329ba111f6e33 Mon Sep 17 00:00:00 2001 From: maurei Date: Sat, 3 Oct 2020 13:49:19 +0200 Subject: [PATCH 21/27] fix: undo --- .../Configuration/ApplicationBuilderExtensions.cs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/JsonApiDotNetCore/Configuration/ApplicationBuilderExtensions.cs b/src/JsonApiDotNetCore/Configuration/ApplicationBuilderExtensions.cs index ea3970ab83..0d4d20f59c 100644 --- a/src/JsonApiDotNetCore/Configuration/ApplicationBuilderExtensions.cs +++ b/src/JsonApiDotNetCore/Configuration/ApplicationBuilderExtensions.cs @@ -23,12 +23,12 @@ public static class ApplicationBuilderExtensions public static void UseJsonApi(this IApplicationBuilder builder) { if (builder == null) throw new ArgumentNullException(nameof(builder)); - + using var scope = builder.ApplicationServices.GetRequiredService().CreateScope(); var inverseRelationshipResolver = scope.ServiceProvider.GetRequiredService(); inverseRelationshipResolver.Resolve(); - - var jsonApiApplicationBuilder = builder.ApplicationServices.GetRequiredService(); + + var jsonApiApplicationBuilder = builder.ApplicationServices.GetRequiredService(); jsonApiApplicationBuilder.ConfigureMvcOptions = options => { var inputFormatter = builder.ApplicationServices.GetRequiredService(); @@ -45,6 +45,3 @@ public static void UseJsonApi(this IApplicationBuilder builder) } } } - - - From f8ebc229a6f0e48cd6336954255c3adcb0ca6ac6 Mon Sep 17 00:00:00 2001 From: maurei Date: Sat, 3 Oct 2020 15:58:13 +0200 Subject: [PATCH 22/27] fix: move files --- .../Configuration/JsonApiApplicationBuilder.cs | 1 - .../Configuration/{Validation => }/JsonApiMetaDataProvider.cs | 2 +- .../Configuration/{Validation => }/JsonApiObjectValidator.cs | 2 +- .../{Validation => }/JsonApiValidationVisitor.cs | 4 ++-- .../{Validation => }/PartialPatchValidationFilter.cs | 2 +- 5 files changed, 5 insertions(+), 6 deletions(-) rename src/JsonApiDotNetCore/Configuration/{Validation => }/JsonApiMetaDataProvider.cs (96%) rename src/JsonApiDotNetCore/Configuration/{Validation => }/JsonApiObjectValidator.cs (98%) rename src/JsonApiDotNetCore/Configuration/{Validation => }/JsonApiValidationVisitor.cs (93%) rename src/JsonApiDotNetCore/Configuration/{Validation => }/PartialPatchValidationFilter.cs (97%) diff --git a/src/JsonApiDotNetCore/Configuration/JsonApiApplicationBuilder.cs b/src/JsonApiDotNetCore/Configuration/JsonApiApplicationBuilder.cs index 1343586d38..f9f707e1e7 100644 --- a/src/JsonApiDotNetCore/Configuration/JsonApiApplicationBuilder.cs +++ b/src/JsonApiDotNetCore/Configuration/JsonApiApplicationBuilder.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using JsonApiDotNetCore.Configuration.Validation; using JsonApiDotNetCore.Hooks.Internal; using JsonApiDotNetCore.Hooks.Internal.Discovery; using JsonApiDotNetCore.Hooks.Internal.Execution; diff --git a/src/JsonApiDotNetCore/Configuration/Validation/JsonApiMetaDataProvider.cs b/src/JsonApiDotNetCore/Configuration/JsonApiMetaDataProvider.cs similarity index 96% rename from src/JsonApiDotNetCore/Configuration/Validation/JsonApiMetaDataProvider.cs rename to src/JsonApiDotNetCore/Configuration/JsonApiMetaDataProvider.cs index e38e03ac2e..cf992f6080 100644 --- a/src/JsonApiDotNetCore/Configuration/Validation/JsonApiMetaDataProvider.cs +++ b/src/JsonApiDotNetCore/Configuration/JsonApiMetaDataProvider.cs @@ -4,7 +4,7 @@ using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; using Microsoft.Extensions.Options; -namespace JsonApiDotNetCore.Configuration.Validation +namespace JsonApiDotNetCore.Configuration { /// /// Custom implementation of that sets an additional diff --git a/src/JsonApiDotNetCore/Configuration/Validation/JsonApiObjectValidator.cs b/src/JsonApiDotNetCore/Configuration/JsonApiObjectValidator.cs similarity index 98% rename from src/JsonApiDotNetCore/Configuration/Validation/JsonApiObjectValidator.cs rename to src/JsonApiDotNetCore/Configuration/JsonApiObjectValidator.cs index 48bafcc124..03de3da118 100644 --- a/src/JsonApiDotNetCore/Configuration/Validation/JsonApiObjectValidator.cs +++ b/src/JsonApiDotNetCore/Configuration/JsonApiObjectValidator.cs @@ -4,7 +4,7 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; -namespace JsonApiDotNetCore.Configuration.Validation +namespace JsonApiDotNetCore.Configuration { /// /// Custom implementation of that is identical to DefaultObjectValidator, apart from diff --git a/src/JsonApiDotNetCore/Configuration/Validation/JsonApiValidationVisitor.cs b/src/JsonApiDotNetCore/Configuration/JsonApiValidationVisitor.cs similarity index 93% rename from src/JsonApiDotNetCore/Configuration/Validation/JsonApiValidationVisitor.cs rename to src/JsonApiDotNetCore/Configuration/JsonApiValidationVisitor.cs index 47984f95bc..e8161af47b 100644 --- a/src/JsonApiDotNetCore/Configuration/Validation/JsonApiValidationVisitor.cs +++ b/src/JsonApiDotNetCore/Configuration/JsonApiValidationVisitor.cs @@ -2,14 +2,14 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; -namespace JsonApiDotNetCore.Configuration.Validation +namespace JsonApiDotNetCore.Configuration { /// /// An extension of the internal that performs an additional check related to /// property validation filters /// /// - /// see **ADD URL TO ASPNETCORE ISSUE RELATED TO THIS** for background information. + /// see https://github.com/dotnet/aspnetcore/issues/26580 for background information. /// internal sealed class JsonApiValidationVisitor : ValidationVisitor { diff --git a/src/JsonApiDotNetCore/Configuration/Validation/PartialPatchValidationFilter.cs b/src/JsonApiDotNetCore/Configuration/PartialPatchValidationFilter.cs similarity index 97% rename from src/JsonApiDotNetCore/Configuration/Validation/PartialPatchValidationFilter.cs rename to src/JsonApiDotNetCore/Configuration/PartialPatchValidationFilter.cs index b3be28f17e..0d8350ac4e 100644 --- a/src/JsonApiDotNetCore/Configuration/Validation/PartialPatchValidationFilter.cs +++ b/src/JsonApiDotNetCore/Configuration/PartialPatchValidationFilter.cs @@ -5,7 +5,7 @@ using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; using Microsoft.Extensions.DependencyInjection; -namespace JsonApiDotNetCore.Configuration.Validation +namespace JsonApiDotNetCore.Configuration { /// /// Validation filter that enables partial patching as part of the json:api spec. From 2868e60e9692952441d433206c7e9dd7318d5c8a Mon Sep 17 00:00:00 2001 From: Bart Koelman Date: Mon, 5 Oct 2020 15:03:26 +0200 Subject: [PATCH 23/27] - Simplified injection - Only inject when ModelState active - Added support for ID validation --- .../JsonApiApplicationBuilder.cs | 10 +- .../Configuration/JsonApiMetaDataProvider.cs | 34 ++++--- .../Configuration/JsonApiObjectValidator.cs | 99 ------------------- .../Configuration/JsonApiValidationFilter.cs | 59 +++++++++++ .../Configuration/JsonApiValidationVisitor.cs | 54 ---------- .../PartialPatchValidationFilter.cs | 57 ----------- .../ModelStateValidationTests.cs | 65 ++++++++++++ .../ModelStateValidation/SystemDirectory.cs | 4 + 8 files changed, 149 insertions(+), 233 deletions(-) delete mode 100644 src/JsonApiDotNetCore/Configuration/JsonApiObjectValidator.cs create mode 100644 src/JsonApiDotNetCore/Configuration/JsonApiValidationFilter.cs delete mode 100644 src/JsonApiDotNetCore/Configuration/JsonApiValidationVisitor.cs delete mode 100644 src/JsonApiDotNetCore/Configuration/PartialPatchValidationFilter.cs diff --git a/src/JsonApiDotNetCore/Configuration/JsonApiApplicationBuilder.cs b/src/JsonApiDotNetCore/Configuration/JsonApiApplicationBuilder.cs index 817cd210e6..f644d4ef5d 100644 --- a/src/JsonApiDotNetCore/Configuration/JsonApiApplicationBuilder.cs +++ b/src/JsonApiDotNetCore/Configuration/JsonApiApplicationBuilder.cs @@ -106,6 +106,7 @@ public void ConfigureMvc() if (_options.ValidateModelState) { _mvcBuilder.AddDataAnnotations(); + _services.AddSingleton(); } } @@ -166,19 +167,12 @@ private void AddMiddlewareLayer() _services.TryAddSingleton(); _services.AddSingleton(sp => sp.GetRequiredService()); _services.AddSingleton(); - _services.AddScoped(); + _services.AddSingleton(); _services.AddScoped(); _services.AddScoped(); _services.AddScoped(); _services.AddScoped(); _services.AddScoped(); - _services.AddSingleton(); - _services.AddSingleton(s => - { - var options = s.GetRequiredService>().Value; - var metadataProvider = s.GetRequiredService(); - return new JsonApiObjectValidator(metadataProvider, options.ModelValidatorProviders, options); - }); } private void AddResourceLayer() diff --git a/src/JsonApiDotNetCore/Configuration/JsonApiMetaDataProvider.cs b/src/JsonApiDotNetCore/Configuration/JsonApiMetaDataProvider.cs index cf992f6080..449035ac2f 100644 --- a/src/JsonApiDotNetCore/Configuration/JsonApiMetaDataProvider.cs +++ b/src/JsonApiDotNetCore/Configuration/JsonApiMetaDataProvider.cs @@ -1,37 +1,41 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata; -using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; using Microsoft.Extensions.Options; namespace JsonApiDotNetCore.Configuration { /// - /// Custom implementation of that sets an additional - /// to support partial patching. + /// Custom implementation of to support json:api partial patching. /// internal class JsonApiModelMetadataProvider : DefaultModelMetadataProvider { + private readonly JsonApiValidationFilter _jsonApiValidationFilter; + /// - public JsonApiModelMetadataProvider(ICompositeMetadataDetailsProvider detailsProvider) - : base(detailsProvider) { } - + public JsonApiModelMetadataProvider(ICompositeMetadataDetailsProvider detailsProvider, IRequestScopedServiceProvider serviceProvider) + : base(detailsProvider) + { + _jsonApiValidationFilter = new JsonApiValidationFilter(serviceProvider); + } + /// - public JsonApiModelMetadataProvider(ICompositeMetadataDetailsProvider detailsProvider, IOptions optionsAccessor) - : base(detailsProvider, optionsAccessor) { } - + public JsonApiModelMetadataProvider(ICompositeMetadataDetailsProvider detailsProvider, IOptions optionsAccessor, IRequestScopedServiceProvider serviceProvider) + : base(detailsProvider, optionsAccessor) + { + _jsonApiValidationFilter = new JsonApiValidationFilter(serviceProvider); + } + /// protected override ModelMetadata CreateModelMetadata(DefaultMetadataDetails entry) { - var metadata = new DefaultModelMetadata(this, DetailsProvider, entry, ModelBindingMessageProvider) ; + var metadata = (DefaultModelMetadata)base.CreateModelMetadata(entry); - var isRequired = metadata.ValidationMetadata.IsRequired; - - if (isRequired != null && isRequired.Value) + if (metadata.ValidationMetadata.IsRequired == true) { - metadata.ValidationMetadata.PropertyValidationFilter = new PartialPatchValidationFilter(); + metadata.ValidationMetadata.PropertyValidationFilter = _jsonApiValidationFilter; } - + return metadata; } } diff --git a/src/JsonApiDotNetCore/Configuration/JsonApiObjectValidator.cs b/src/JsonApiDotNetCore/Configuration/JsonApiObjectValidator.cs deleted file mode 100644 index 03de3da118..0000000000 --- a/src/JsonApiDotNetCore/Configuration/JsonApiObjectValidator.cs +++ /dev/null @@ -1,99 +0,0 @@ -using System; -using System.Collections.Generic; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.ModelBinding; -using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; - -namespace JsonApiDotNetCore.Configuration -{ - /// - /// Custom implementation of that is identical to DefaultObjectValidator, apart from - /// using our own instead of the built-in . - /// - /// - /// See https://github.com/dotnet/aspnetcore/blob/v3.1.8/src/Mvc/Mvc.Core/src/ModelBinding/Validation/DefaultObjectValidator.cs - /// - internal class JsonApiObjectValidator : ObjectModelValidator - { - private readonly IModelMetadataProvider _modelMetadataProvider; - private readonly MvcOptions _mvcOptions; - private readonly ValidatorCache _validatorCache; - private readonly CompositeModelValidatorProvider _validatorProvider; - - /// - public JsonApiObjectValidator( - IModelMetadataProvider modelMetadataProvider, - IList validatorProviders, - MvcOptions mvcOptions) - : base(modelMetadataProvider, validatorProviders) - { - if (validatorProviders == null) - { - throw new ArgumentNullException(nameof(validatorProviders)); - } - - _modelMetadataProvider = modelMetadataProvider ?? throw new ArgumentNullException(nameof(modelMetadataProvider)); - _validatorCache = new ValidatorCache(); - _validatorProvider = new CompositeModelValidatorProvider(validatorProviders); - _mvcOptions = mvcOptions; - } - - /// - public override ValidationVisitor GetValidationVisitor( - ActionContext actionContext, - IModelValidatorProvider validatorProvider, - ValidatorCache validatorCache, - IModelMetadataProvider metadataProvider, - ValidationStateDictionary validationState) - { - var visitor = new JsonApiValidationVisitor( - actionContext, - validatorProvider, - validatorCache, - metadataProvider, - validationState) - { - MaxValidationDepth = _mvcOptions.MaxValidationDepth, - ValidateComplexTypesIfChildValidationFails = _mvcOptions.ValidateComplexTypesIfChildValidationFails, - }; - - return visitor; - } - - /// - public override void Validate( - ActionContext actionContext, - ValidationStateDictionary validationState, - string prefix, - object model) - { - var visitor = GetValidationVisitor( - actionContext, - _validatorProvider, - _validatorCache, - _modelMetadataProvider, - validationState); - - var metadata = model == null ? null : _modelMetadataProvider.GetMetadataForType(model.GetType()); - visitor.Validate(metadata, prefix, model, alwaysValidateAtTopLevel: false); - } - - /// - public override void Validate( - ActionContext actionContext, - ValidationStateDictionary validationState, - string prefix, - object model, - ModelMetadata metadata) - { - var visitor = GetValidationVisitor( - actionContext, - _validatorProvider, - _validatorCache, - _modelMetadataProvider, - validationState); - - visitor.Validate(metadata, prefix, model, alwaysValidateAtTopLevel: metadata.IsRequired); - } - } -} diff --git a/src/JsonApiDotNetCore/Configuration/JsonApiValidationFilter.cs b/src/JsonApiDotNetCore/Configuration/JsonApiValidationFilter.cs new file mode 100644 index 0000000000..b4af0ea6f5 --- /dev/null +++ b/src/JsonApiDotNetCore/Configuration/JsonApiValidationFilter.cs @@ -0,0 +1,59 @@ +using System; +using System.Linq; +using JsonApiDotNetCore.Middleware; +using JsonApiDotNetCore.Resources; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; +using Microsoft.Extensions.DependencyInjection; + +namespace JsonApiDotNetCore.Configuration +{ + /// + /// Validation filter that blocks ASP.NET Core ModelState validation on data according to the json:api spec. + /// + internal sealed class JsonApiValidationFilter : IPropertyValidationFilter + { + private readonly IRequestScopedServiceProvider _serviceProvider; + + public JsonApiValidationFilter(IRequestScopedServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); + } + + /// + public bool ShouldValidateEntry(ValidationEntry entry, ValidationEntry parentEntry) + { + var request = _serviceProvider.GetRequiredService(); + + if (IsId(entry.Key)) + { + return true; + } + + var isTopResourceInPrimaryRequest = string.IsNullOrEmpty(parentEntry.Key) && request.Kind == EndpointKind.Primary; + if (!isTopResourceInPrimaryRequest) + { + return false; + } + + var httpContextAccessor = _serviceProvider.GetRequiredService(); + if (httpContextAccessor.HttpContext.Request.Method == HttpMethods.Patch) + { + var targetedFields = _serviceProvider.GetRequiredService(); + return IsFieldTargeted(entry, targetedFields); + } + + return true; + } + + private static bool IsId(string key) + { + return key == nameof(Identifiable.Id) || key.EndsWith("." + nameof(Identifiable.Id), StringComparison.Ordinal); + } + + private static bool IsFieldTargeted(ValidationEntry entry, ITargetedFields targetedFields) + { + return targetedFields.Attributes.Any(attribute => attribute.Property.Name == entry.Key); + } + } +} diff --git a/src/JsonApiDotNetCore/Configuration/JsonApiValidationVisitor.cs b/src/JsonApiDotNetCore/Configuration/JsonApiValidationVisitor.cs deleted file mode 100644 index e8161af47b..0000000000 --- a/src/JsonApiDotNetCore/Configuration/JsonApiValidationVisitor.cs +++ /dev/null @@ -1,54 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.ModelBinding; -using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; - -namespace JsonApiDotNetCore.Configuration -{ - /// - /// An extension of the internal that performs an additional check related to - /// property validation filters - /// - /// - /// see https://github.com/dotnet/aspnetcore/issues/26580 for background information. - /// - internal sealed class JsonApiValidationVisitor : ValidationVisitor - { - /// - public JsonApiValidationVisitor( - ActionContext actionContext, - IModelValidatorProvider validatorProvider, - ValidatorCache validatorCache, - IModelMetadataProvider metadataProvider, - ValidationStateDictionary validationState) - : base(actionContext, validatorProvider, validatorCache, metadataProvider, validationState) { } - - /// - protected override bool VisitChildren(IValidationStrategy strategy) - { - var isValid = true; - var enumerator = strategy.GetChildren(Metadata, Key, Model); - var parentEntry = new ValidationEntry(Metadata, Key, Model); - - while (enumerator.MoveNext()) - { - var entry = enumerator.Current; - var metadata = entry.Metadata; - var key = entry.Key; - - var jsonApiFilter = metadata.PropertyValidationFilter as PartialPatchValidationFilter; - var serviceProvider = Context?.HttpContext?.RequestServices; - - if (metadata.PropertyValidationFilter?.ShouldValidateEntry(entry, parentEntry) == false - || jsonApiFilter != null && jsonApiFilter.ShouldValidateEntry(entry, parentEntry, serviceProvider) == false ) - { - SuppressValidation(key); - continue; - } - - isValid &= Visit(metadata, key, entry.Model); - } - - return isValid; - } - } -} diff --git a/src/JsonApiDotNetCore/Configuration/PartialPatchValidationFilter.cs b/src/JsonApiDotNetCore/Configuration/PartialPatchValidationFilter.cs deleted file mode 100644 index 0d8350ac4e..0000000000 --- a/src/JsonApiDotNetCore/Configuration/PartialPatchValidationFilter.cs +++ /dev/null @@ -1,57 +0,0 @@ -using System; -using JsonApiDotNetCore.Middleware; -using JsonApiDotNetCore.Resources; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; -using Microsoft.Extensions.DependencyInjection; - -namespace JsonApiDotNetCore.Configuration -{ - /// - /// Validation filter that enables partial patching as part of the json:api spec. - /// - internal sealed class PartialPatchValidationFilter : IPropertyValidationFilter - { - /// - public bool ShouldValidateEntry(ValidationEntry entry, ValidationEntry parentEntry) => true; - - public bool ShouldValidateEntry(ValidationEntry entry, ValidationEntry parentEntry, IServiceProvider serviceProvider) - { - if (serviceProvider == null) throw new ArgumentException(nameof(serviceProvider)); - - var request = serviceProvider.GetRequiredService(); - var httpContextAccessor = serviceProvider.GetRequiredService(); - var targetedFields = serviceProvider.GetRequiredService(); - - if (request.Kind == EndpointKind.Primary && string.IsNullOrEmpty(parentEntry.Key) && RequiredFieldIsTargeted(entry, targetedFields, httpContextAccessor)) - { - return true; - } - - return false; - } - - private bool RequiredFieldIsTargeted(ValidationEntry entry, ITargetedFields targetedFields, IHttpContextAccessor httpContextAccessor) - { - var requestMethod = httpContextAccessor.HttpContext.Request.Method; - - if (requestMethod == HttpMethods.Post) - { - return true; - } - - if (requestMethod == HttpMethods.Patch) - { - foreach (var attribute in targetedFields.Attributes) - { - if (attribute.Property.Name == entry.Key) - { - return true; - } - } - } - - return false; - } - } -} diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ModelStateValidation/ModelStateValidationTests.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ModelStateValidation/ModelStateValidationTests.cs index e85da62d5b..5f2d74715a 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ModelStateValidation/ModelStateValidationTests.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ModelStateValidation/ModelStateValidationTests.cs @@ -410,6 +410,71 @@ await _testContext.RunOnDatabaseAsync(async dbContext => responseDocument.Errors[0].Source.Pointer.Should().Be("/data/attributes/name"); } + [Fact] + public async Task When_patching_resource_with_invalid_ID_it_must_fail() + { + // Arrange + var directory = new SystemDirectory + { + Name = "Projects", + IsCaseSensitive = true + }; + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.Directories.Add(directory); + await dbContext.SaveChangesAsync(); + }); + + var content = new + { + data = new + { + type = "systemDirectories", + id = -1, + attributes = new Dictionary + { + ["name"] = "Repositories" + }, + relationships = new Dictionary + { + ["subdirectories"] = new + { + data = new[] + { + new + { + type = "systemDirectories", + id = -1 + } + } + } + } + } + }; + + string requestBody = JsonConvert.SerializeObject(content); + string route = "/systemDirectories/-1"; + + // Act + var (httpResponse, responseDocument) = await _testContext.ExecutePatchAsync(route, requestBody); + + // Assert + httpResponse.Should().HaveStatusCode(HttpStatusCode.UnprocessableEntity); + + responseDocument.Errors.Should().HaveCount(2); + + responseDocument.Errors[0].StatusCode.Should().Be(HttpStatusCode.UnprocessableEntity); + responseDocument.Errors[0].Title.Should().Be("Input validation failed."); + responseDocument.Errors[0].Detail.Should().Be("The field Id must match the regular expression '^[0-9]+$'."); + responseDocument.Errors[0].Source.Pointer.Should().Be("/data/attributes/id"); + + responseDocument.Errors[1].StatusCode.Should().Be(HttpStatusCode.UnprocessableEntity); + responseDocument.Errors[1].Title.Should().Be("Input validation failed."); + responseDocument.Errors[1].Detail.Should().Be("The field Id must match the regular expression '^[0-9]+$'."); + responseDocument.Errors[1].Source.Pointer.Should().Be("/data/attributes/Subdirectories[0].Id"); + } + [Fact] public async Task When_patching_resource_with_valid_attribute_value_it_must_succeed() { diff --git a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ModelStateValidation/SystemDirectory.cs b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ModelStateValidation/SystemDirectory.cs index ddc036d27f..9c851fe832 100644 --- a/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ModelStateValidation/SystemDirectory.cs +++ b/test/JsonApiDotNetCoreExampleTests/IntegrationTests/ModelStateValidation/SystemDirectory.cs @@ -7,6 +7,10 @@ namespace JsonApiDotNetCoreExampleTests.IntegrationTests.ModelStateValidation { public sealed class SystemDirectory : Identifiable { + [Required] + [RegularExpression("^[0-9]+$")] + public override int Id { get; set; } + [Attr] [Required] [RegularExpression(@"^[\w\s]+$")] From 0b8ea46b316b66af6fc224858a19272110a2ebd2 Mon Sep 17 00:00:00 2001 From: Bart Koelman Date: Mon, 5 Oct 2020 15:04:14 +0200 Subject: [PATCH 24/27] Removed unneeded override --- src/JsonApiDotNetCore/Serialization/BaseDeserializer.cs | 2 +- .../Serialization/RequestDeserializer.cs | 8 -------- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/src/JsonApiDotNetCore/Serialization/BaseDeserializer.cs b/src/JsonApiDotNetCore/Serialization/BaseDeserializer.cs index 5e2e94a757..61d6ec1408 100644 --- a/src/JsonApiDotNetCore/Serialization/BaseDeserializer.cs +++ b/src/JsonApiDotNetCore/Serialization/BaseDeserializer.cs @@ -68,7 +68,7 @@ protected object DeserializeBody(string body) /// The parsed resource. /// Attributes and their values, as in the serialized content. /// Exposed attributes for . - protected virtual IIdentifiable SetAttributes(IIdentifiable resource, IDictionary attributeValues, IReadOnlyCollection attributes) + protected IIdentifiable SetAttributes(IIdentifiable resource, IDictionary attributeValues, IReadOnlyCollection attributes) { if (resource == null) throw new ArgumentNullException(nameof(resource)); if (attributes == null) throw new ArgumentNullException(nameof(attributes)); diff --git a/src/JsonApiDotNetCore/Serialization/RequestDeserializer.cs b/src/JsonApiDotNetCore/Serialization/RequestDeserializer.cs index acea480c5a..aec884ecba 100644 --- a/src/JsonApiDotNetCore/Serialization/RequestDeserializer.cs +++ b/src/JsonApiDotNetCore/Serialization/RequestDeserializer.cs @@ -65,13 +65,5 @@ protected override void AfterProcessField(IIdentifiable resource, ResourceFieldA else if (field is RelationshipAttribute relationship) _targetedFields.Relationships.Add(relationship); } - - protected override IIdentifiable SetAttributes(IIdentifiable resource, IDictionary attributeValues, IReadOnlyCollection attributes) - { - if (resource == null) throw new ArgumentNullException(nameof(resource)); - if (attributes == null) throw new ArgumentNullException(nameof(attributes)); - - return base.SetAttributes(resource, attributeValues, attributes); - } } } From 2288fbbeb2788f1736624737176667439319d60e Mon Sep 17 00:00:00 2001 From: Bart Koelman Date: Mon, 5 Oct 2020 15:05:21 +0200 Subject: [PATCH 25/27] Fix filename casing 1/2 --- .../{JsonApiMetaDataProvider.cs => JsonApiMetadataProvider2.cs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/JsonApiDotNetCore/Configuration/{JsonApiMetaDataProvider.cs => JsonApiMetadataProvider2.cs} (100%) diff --git a/src/JsonApiDotNetCore/Configuration/JsonApiMetaDataProvider.cs b/src/JsonApiDotNetCore/Configuration/JsonApiMetadataProvider2.cs similarity index 100% rename from src/JsonApiDotNetCore/Configuration/JsonApiMetaDataProvider.cs rename to src/JsonApiDotNetCore/Configuration/JsonApiMetadataProvider2.cs From 3220825cc5bb7e7f329e03cffd91f4d9c5db6ba1 Mon Sep 17 00:00:00 2001 From: Bart Koelman Date: Mon, 5 Oct 2020 15:05:38 +0200 Subject: [PATCH 26/27] Fix filename casing 2/2 --- .../{JsonApiMetadataProvider2.cs => JsonApiMetadataProvider.cs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/JsonApiDotNetCore/Configuration/{JsonApiMetadataProvider2.cs => JsonApiMetadataProvider.cs} (100%) diff --git a/src/JsonApiDotNetCore/Configuration/JsonApiMetadataProvider2.cs b/src/JsonApiDotNetCore/Configuration/JsonApiMetadataProvider.cs similarity index 100% rename from src/JsonApiDotNetCore/Configuration/JsonApiMetadataProvider2.cs rename to src/JsonApiDotNetCore/Configuration/JsonApiMetadataProvider.cs From c1bd74a51392e4c02bdaad21438c09a45e7fd74f Mon Sep 17 00:00:00 2001 From: Bart Koelman Date: Mon, 5 Oct 2020 15:10:04 +0200 Subject: [PATCH 27/27] Removed unused usings --- .../Configuration/JsonApiApplicationBuilder.cs | 2 -- .../Configuration/ServiceCollectionExtensions.cs | 2 -- src/JsonApiDotNetCore/Serialization/RequestDeserializer.cs | 1 - test/UnitTests/Serialization/Client/RequestSerializerTests.cs | 1 - .../Serialization/Common/ResourceObjectBuilderTests.cs | 2 -- 5 files changed, 8 deletions(-) diff --git a/src/JsonApiDotNetCore/Configuration/JsonApiApplicationBuilder.cs b/src/JsonApiDotNetCore/Configuration/JsonApiApplicationBuilder.cs index f644d4ef5d..eb742cacd4 100644 --- a/src/JsonApiDotNetCore/Configuration/JsonApiApplicationBuilder.cs +++ b/src/JsonApiDotNetCore/Configuration/JsonApiApplicationBuilder.cs @@ -18,12 +18,10 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.ModelBinding; -using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; namespace JsonApiDotNetCore.Configuration { diff --git a/src/JsonApiDotNetCore/Configuration/ServiceCollectionExtensions.cs b/src/JsonApiDotNetCore/Configuration/ServiceCollectionExtensions.cs index ffa5fd340e..1c1e77f436 100644 --- a/src/JsonApiDotNetCore/Configuration/ServiceCollectionExtensions.cs +++ b/src/JsonApiDotNetCore/Configuration/ServiceCollectionExtensions.cs @@ -1,10 +1,8 @@ using System; using System.Collections.Generic; -using System.ComponentModel.Design; using System.Linq; using System.Reflection; using JsonApiDotNetCore.Errors; -using JsonApiDotNetCore.Resources; using JsonApiDotNetCore.Serialization.Building; using JsonApiDotNetCore.Serialization.Client.Internal; using JsonApiDotNetCore.Services; diff --git a/src/JsonApiDotNetCore/Serialization/RequestDeserializer.cs b/src/JsonApiDotNetCore/Serialization/RequestDeserializer.cs index aec884ecba..6c4a26796e 100644 --- a/src/JsonApiDotNetCore/Serialization/RequestDeserializer.cs +++ b/src/JsonApiDotNetCore/Serialization/RequestDeserializer.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Net.Http; using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Errors; diff --git a/test/UnitTests/Serialization/Client/RequestSerializerTests.cs b/test/UnitTests/Serialization/Client/RequestSerializerTests.cs index 0089f70262..e5a2d696ce 100644 --- a/test/UnitTests/Serialization/Client/RequestSerializerTests.cs +++ b/test/UnitTests/Serialization/Client/RequestSerializerTests.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using System.ComponentModel.Design; using System.Text.RegularExpressions; using JsonApiDotNetCore.Resources; using JsonApiDotNetCore.Serialization.Building; diff --git a/test/UnitTests/Serialization/Common/ResourceObjectBuilderTests.cs b/test/UnitTests/Serialization/Common/ResourceObjectBuilderTests.cs index 1e884c28f1..58d380cee9 100644 --- a/test/UnitTests/Serialization/Common/ResourceObjectBuilderTests.cs +++ b/test/UnitTests/Serialization/Common/ResourceObjectBuilderTests.cs @@ -1,9 +1,7 @@ using System; using System.Collections; using System.Collections.Generic; -using System.ComponentModel.Design; using System.Linq; -using JsonApiDotNetCore.Resources; using JsonApiDotNetCore.Serialization.Building; using JsonApiDotNetCore.Serialization.Objects; using UnitTests.TestModels;