From 1742ad9c086742b691323bb27c783924b9135f68 Mon Sep 17 00:00:00 2001 From: Jass Bagga Date: Wed, 1 Feb 2017 16:45:56 -0800 Subject: [PATCH 1/6] Add SpecialParametersBindingMetadataProvider --- .../ModelBinding/BindingSource.cs | 9 ++++ .../Properties/Resources.Designer.cs | 16 +++++++ .../Resources.resx | 3 ++ .../Internal/MvcCoreMvcOptionsSetup.cs | 4 ++ ...pecialParametersBindingMetadataProvider.cs | 44 +++++++++++++++++++ .../MvcOptionsSetupTest.cs | 20 +++++++++ 6 files changed, 96 insertions(+) create mode 100644 src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/Metadata/SpecialParametersBindingMetadataProvider.cs diff --git a/src/Microsoft.AspNetCore.Mvc.Abstractions/ModelBinding/BindingSource.cs b/src/Microsoft.AspNetCore.Mvc.Abstractions/ModelBinding/BindingSource.cs index dc08b98db8..f6045d3bb7 100644 --- a/src/Microsoft.AspNetCore.Mvc.Abstractions/ModelBinding/BindingSource.cs +++ b/src/Microsoft.AspNetCore.Mvc.Abstractions/ModelBinding/BindingSource.cs @@ -86,6 +86,15 @@ public class BindingSource : IEquatable isGreedy: true, isFromRequest: false); + /// + /// A for special parameter types that are not user input. + /// + public static readonly BindingSource Special = new BindingSource( + "Special", + Resources.BindingSource_Special, + isGreedy: true, + isFromRequest: false); + /// /// Creates a new . /// diff --git a/src/Microsoft.AspNetCore.Mvc.Abstractions/Properties/Resources.Designer.cs b/src/Microsoft.AspNetCore.Mvc.Abstractions/Properties/Resources.Designer.cs index fd85073dde..25e41022f8 100644 --- a/src/Microsoft.AspNetCore.Mvc.Abstractions/Properties/Resources.Designer.cs +++ b/src/Microsoft.AspNetCore.Mvc.Abstractions/Properties/Resources.Designer.cs @@ -170,6 +170,22 @@ internal static string FormatBindingSource_Services() return GetString("BindingSource_Services"); } + /// + /// Special + /// + internal static string BindingSource_Special + { + get { return GetString("BindingSource_Special"); } + } + + /// + /// Special + /// + internal static string FormatBindingSource_Special() + { + return GetString("BindingSource_Special"); + } + /// /// ModelBinding /// diff --git a/src/Microsoft.AspNetCore.Mvc.Abstractions/Resources.resx b/src/Microsoft.AspNetCore.Mvc.Abstractions/Resources.resx index 6368aa3629..28b16a928c 100644 --- a/src/Microsoft.AspNetCore.Mvc.Abstractions/Resources.resx +++ b/src/Microsoft.AspNetCore.Mvc.Abstractions/Resources.resx @@ -168,4 +168,7 @@ The provided binding source '{0}' is not a greedy data source. '{1}' only supports greedy data sources. + + Special + \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Internal/MvcCoreMvcOptionsSetup.cs b/src/Microsoft.AspNetCore.Mvc.Core/Internal/MvcCoreMvcOptionsSetup.cs index 91e138301b..d26c243a57 100644 --- a/src/Microsoft.AspNetCore.Mvc.Core/Internal/MvcCoreMvcOptionsSetup.cs +++ b/src/Microsoft.AspNetCore.Mvc.Core/Internal/MvcCoreMvcOptionsSetup.cs @@ -79,6 +79,10 @@ public void Configure(MvcOptions options) options.ModelMetadataDetailsProviders.Add(new DefaultBindingMetadataProvider()); options.ModelMetadataDetailsProviders.Add(new DefaultValidationMetadataProvider()); + options.ModelMetadataDetailsProviders.Add(new SpecialParametersBindingMetadataProvider(typeof(CancellationToken))); + options.ModelMetadataDetailsProviders.Add(new SpecialParametersBindingMetadataProvider(typeof(IFormFile))); + options.ModelMetadataDetailsProviders.Add(new SpecialParametersBindingMetadataProvider(typeof(IFormCollection))); + options.ModelMetadataDetailsProviders.Add(new SpecialParametersBindingMetadataProvider(typeof(Stream))); // Set up validators options.ModelValidatorProviders.Add(new DefaultModelValidatorProvider()); diff --git a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/Metadata/SpecialParametersBindingMetadataProvider.cs b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/Metadata/SpecialParametersBindingMetadataProvider.cs new file mode 100644 index 0000000000..3b64ac506f --- /dev/null +++ b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/Metadata/SpecialParametersBindingMetadataProvider.cs @@ -0,0 +1,44 @@ +// 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.Reflection; + +namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata +{ + public class SpecialParametersBindingMetadataProvider : IBindingMetadataProvider + { + public Type Type { get; } + + /// + /// Creates a new for the given . + /// + /// + /// The . All properties with this will have + /// set to . + /// + public SpecialParametersBindingMetadataProvider(Type type) + { + if (type == null) + { + throw new ArgumentNullException(nameof(type)); + } + + Type = type; + } + + /// + public void CreateBindingMetadata(BindingMetadataProviderContext context) + { + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + + if (Type.IsAssignableFrom(context.Key.ModelType)) + { + context.BindingMetadata.BindingSource = BindingSource.Special; + } + } + } +} diff --git a/test/Microsoft.AspNetCore.Mvc.Test/MvcOptionsSetupTest.cs b/test/Microsoft.AspNetCore.Mvc.Test/MvcOptionsSetupTest.cs index 69450a51c3..ca1150c883 100644 --- a/test/Microsoft.AspNetCore.Mvc.Test/MvcOptionsSetupTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.Test/MvcOptionsSetupTest.cs @@ -162,6 +162,26 @@ public void Setup_SetsUpMetadataDetailsProviders() provider => Assert.IsType(provider), provider => Assert.IsType(provider), provider => + { + var specialParameter = Assert.IsType(provider); + Assert.Equal(typeof(CancellationToken), specialParameter.Type); + }, + provider => + { + var specialParameter = Assert.IsType(provider); + Assert.Equal(typeof(IFormFile), specialParameter.Type); + }, + provider => + { + var specialParameter = Assert.IsType(provider); + Assert.Equal(typeof(IFormCollection), specialParameter.Type); + }, + provider => + { + var specialParameter = Assert.IsType(provider); + Assert.Equal(typeof(Stream), specialParameter.Type); + }, + provider => { var excludeFilter = Assert.IsType(provider); Assert.Equal(typeof(Type), excludeFilter.Type); From ef2ee2955f7855c848e681bd06ddfd1be1e80d09 Mon Sep 17 00:00:00 2001 From: Jass Bagga Date: Wed, 1 Feb 2017 17:12:59 -0800 Subject: [PATCH 2/6] Formatting --- .../Internal/MvcCoreMvcOptionsSetup.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Internal/MvcCoreMvcOptionsSetup.cs b/src/Microsoft.AspNetCore.Mvc.Core/Internal/MvcCoreMvcOptionsSetup.cs index d26c243a57..9a4713548b 100644 --- a/src/Microsoft.AspNetCore.Mvc.Core/Internal/MvcCoreMvcOptionsSetup.cs +++ b/src/Microsoft.AspNetCore.Mvc.Core/Internal/MvcCoreMvcOptionsSetup.cs @@ -83,6 +83,7 @@ public void Configure(MvcOptions options) options.ModelMetadataDetailsProviders.Add(new SpecialParametersBindingMetadataProvider(typeof(IFormFile))); options.ModelMetadataDetailsProviders.Add(new SpecialParametersBindingMetadataProvider(typeof(IFormCollection))); options.ModelMetadataDetailsProviders.Add(new SpecialParametersBindingMetadataProvider(typeof(Stream))); + // Set up validators options.ModelValidatorProviders.Add(new DefaultModelValidatorProvider()); From 8b9acbbf0fbd83c5065faf5a8beb4efc453cd211 Mon Sep 17 00:00:00 2001 From: Jass Bagga Date: Fri, 3 Feb 2017 14:53:06 -0800 Subject: [PATCH 3/6] Add FormFileBindingSourceMetadataProvider --- .../ModelBinding/BindingSource.cs | 10 +++++ .../Properties/Resources.Designer.cs | 16 +++++++ .../Resources.resx | 3 ++ .../Internal/MvcCoreMvcOptionsSetup.cs | 7 ++- .../FormFileBindingSourceMetadataProvider.cs | 45 +++++++++++++++++++ ...> SpecialBindingSourceMetadataProvider.cs} | 11 ++--- .../MvcOptionsSetupTest.cs | 15 +++---- 7 files changed, 88 insertions(+), 19 deletions(-) create mode 100644 src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/Metadata/FormFileBindingSourceMetadataProvider.cs rename src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/Metadata/{SpecialParametersBindingMetadataProvider.cs => SpecialBindingSourceMetadataProvider.cs} (67%) diff --git a/src/Microsoft.AspNetCore.Mvc.Abstractions/ModelBinding/BindingSource.cs b/src/Microsoft.AspNetCore.Mvc.Abstractions/ModelBinding/BindingSource.cs index f6045d3bb7..87791e85da 100644 --- a/src/Microsoft.AspNetCore.Mvc.Abstractions/ModelBinding/BindingSource.cs +++ b/src/Microsoft.AspNetCore.Mvc.Abstractions/ModelBinding/BindingSource.cs @@ -3,6 +3,7 @@ using System; using System.Diagnostics; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.Abstractions; namespace Microsoft.AspNetCore.Mvc.ModelBinding @@ -95,6 +96,15 @@ public class BindingSource : IEquatable isGreedy: true, isFromRequest: false); + /// + /// A for and . + /// + public static readonly BindingSource FormFile = new BindingSource( + "FormFile", + Resources.BindingSource_FormFile, + isGreedy: true, + isFromRequest: true); + /// /// Creates a new . /// diff --git a/src/Microsoft.AspNetCore.Mvc.Abstractions/Properties/Resources.Designer.cs b/src/Microsoft.AspNetCore.Mvc.Abstractions/Properties/Resources.Designer.cs index 25e41022f8..3cd4baf115 100644 --- a/src/Microsoft.AspNetCore.Mvc.Abstractions/Properties/Resources.Designer.cs +++ b/src/Microsoft.AspNetCore.Mvc.Abstractions/Properties/Resources.Designer.cs @@ -186,6 +186,22 @@ internal static string FormatBindingSource_Special() return GetString("BindingSource_Special"); } + /// + /// FormFile + /// + internal static string BindingSource_FormFile + { + get { return GetString("BindingSource_FormFile"); } + } + + /// + /// FormFile + /// + internal static string FormatBindingSource_FormFile() + { + return GetString("BindingSource_FormFile"); + } + /// /// ModelBinding /// diff --git a/src/Microsoft.AspNetCore.Mvc.Abstractions/Resources.resx b/src/Microsoft.AspNetCore.Mvc.Abstractions/Resources.resx index 28b16a928c..224ec4161d 100644 --- a/src/Microsoft.AspNetCore.Mvc.Abstractions/Resources.resx +++ b/src/Microsoft.AspNetCore.Mvc.Abstractions/Resources.resx @@ -171,4 +171,7 @@ Special + + FormFile + \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Internal/MvcCoreMvcOptionsSetup.cs b/src/Microsoft.AspNetCore.Mvc.Core/Internal/MvcCoreMvcOptionsSetup.cs index 9a4713548b..7f8572157d 100644 --- a/src/Microsoft.AspNetCore.Mvc.Core/Internal/MvcCoreMvcOptionsSetup.cs +++ b/src/Microsoft.AspNetCore.Mvc.Core/Internal/MvcCoreMvcOptionsSetup.cs @@ -79,10 +79,9 @@ public void Configure(MvcOptions options) options.ModelMetadataDetailsProviders.Add(new DefaultBindingMetadataProvider()); options.ModelMetadataDetailsProviders.Add(new DefaultValidationMetadataProvider()); - options.ModelMetadataDetailsProviders.Add(new SpecialParametersBindingMetadataProvider(typeof(CancellationToken))); - options.ModelMetadataDetailsProviders.Add(new SpecialParametersBindingMetadataProvider(typeof(IFormFile))); - options.ModelMetadataDetailsProviders.Add(new SpecialParametersBindingMetadataProvider(typeof(IFormCollection))); - options.ModelMetadataDetailsProviders.Add(new SpecialParametersBindingMetadataProvider(typeof(Stream))); + options.ModelMetadataDetailsProviders.Add(new SpecialBindingSourceMetadataProvider(typeof(CancellationToken))); + options.ModelMetadataDetailsProviders.Add(new FormFileBindingSourceMetadataProvider(typeof(IFormFile))); + options.ModelMetadataDetailsProviders.Add(new FormFileBindingSourceMetadataProvider(typeof(IFormCollection))); // Set up validators options.ModelValidatorProviders.Add(new DefaultModelValidatorProvider()); diff --git a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/Metadata/FormFileBindingSourceMetadataProvider.cs b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/Metadata/FormFileBindingSourceMetadataProvider.cs new file mode 100644 index 0000000000..4d4c9521d1 --- /dev/null +++ b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/Metadata/FormFileBindingSourceMetadataProvider.cs @@ -0,0 +1,45 @@ +// 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.Reflection; +using Microsoft.AspNetCore.Http; + +namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata +{ + public class FormFileBindingSourceMetadataProvider : IBindingMetadataProvider + { + public Type Type { get; } + + /// + /// Creates a new for the given . + /// + /// + /// The . The provider sets to + /// for properties of and . + /// + public FormFileBindingSourceMetadataProvider(Type type) + { + if (type == null) + { + throw new ArgumentNullException(nameof(type)); + } + + Type = type; + } + + /// + public void CreateBindingMetadata(BindingMetadataProviderContext context) + { + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + + if (Type.IsAssignableFrom(context.Key.ModelType)) + { + context.BindingMetadata.BindingSource = BindingSource.FormFile; + } + } + } +} diff --git a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/Metadata/SpecialParametersBindingMetadataProvider.cs b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/Metadata/SpecialBindingSourceMetadataProvider.cs similarity index 67% rename from src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/Metadata/SpecialParametersBindingMetadataProvider.cs rename to src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/Metadata/SpecialBindingSourceMetadataProvider.cs index 3b64ac506f..f329c65478 100644 --- a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/Metadata/SpecialParametersBindingMetadataProvider.cs +++ b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/Metadata/SpecialBindingSourceMetadataProvider.cs @@ -3,21 +3,22 @@ using System; using System.Reflection; +using System.Threading; namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata { - public class SpecialParametersBindingMetadataProvider : IBindingMetadataProvider + public class SpecialBindingSourceMetadataProvider : IBindingMetadataProvider { public Type Type { get; } /// - /// Creates a new for the given . + /// Creates a new for the given . /// /// - /// The . All properties with this will have - /// set to . + /// The . The provider sets to + /// for properties of . /// - public SpecialParametersBindingMetadataProvider(Type type) + public SpecialBindingSourceMetadataProvider(Type type) { if (type == null) { diff --git a/test/Microsoft.AspNetCore.Mvc.Test/MvcOptionsSetupTest.cs b/test/Microsoft.AspNetCore.Mvc.Test/MvcOptionsSetupTest.cs index ca1150c883..a25cbe1586 100644 --- a/test/Microsoft.AspNetCore.Mvc.Test/MvcOptionsSetupTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.Test/MvcOptionsSetupTest.cs @@ -163,23 +163,18 @@ public void Setup_SetsUpMetadataDetailsProviders() provider => Assert.IsType(provider), provider => { - var specialParameter = Assert.IsType(provider); + var specialParameter = Assert.IsType(provider); Assert.Equal(typeof(CancellationToken), specialParameter.Type); }, provider => { - var specialParameter = Assert.IsType(provider); - Assert.Equal(typeof(IFormFile), specialParameter.Type); + var formFileParameter = Assert.IsType(provider); + Assert.Equal(typeof(IFormFile), formFileParameter.Type); }, provider => { - var specialParameter = Assert.IsType(provider); - Assert.Equal(typeof(IFormCollection), specialParameter.Type); - }, - provider => - { - var specialParameter = Assert.IsType(provider); - Assert.Equal(typeof(Stream), specialParameter.Type); + var formCollectionParameter = Assert.IsType(provider); + Assert.Equal(typeof(IFormCollection), formCollectionParameter.Type); }, provider => { From d475f0053d75e3a5ab273b7782b54fa575940caa Mon Sep 17 00:00:00 2001 From: Jass Bagga Date: Thu, 9 Feb 2017 17:10:46 -0800 Subject: [PATCH 4/6] Added unit tests --- ...mFileBindingSourceMetadataProviderTests.cs | 32 +++++++++++++++++++ ...ecialBindingSourceMetadataProviderTests.cs | 32 +++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 test/Microsoft.AspNetCore.Mvc.Core.Test/ModelBinding/Metadata/FormFileBindingSourceMetadataProviderTests.cs create mode 100644 test/Microsoft.AspNetCore.Mvc.Core.Test/ModelBinding/Metadata/SpecialBindingSourceMetadataProviderTests.cs diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/ModelBinding/Metadata/FormFileBindingSourceMetadataProviderTests.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/ModelBinding/Metadata/FormFileBindingSourceMetadataProviderTests.cs new file mode 100644 index 0000000000..c7fa3fdc49 --- /dev/null +++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/ModelBinding/Metadata/FormFileBindingSourceMetadataProviderTests.cs @@ -0,0 +1,32 @@ +// 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 Xunit; + +namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata +{ + public class FormFileBindingSourceMetadataProviderTests + { + [Fact] + public void ChecksParameterType_AssignsFormFileBindingSource() + { + // Arrange + var provider = new FormFileBindingSourceMetadataProvider(typeof(Test)); + + var key = ModelMetadataIdentity.ForType( + typeof(Test)); + + var context = new BindingMetadataProviderContext(key, new ModelAttributes(new object[0], new object[0])); + + // Act + provider.CreateBindingMetadata(context); + + // Assert + Assert.Equal(BindingSource.FormFile, context.BindingMetadata.BindingSource); + } + + private class Test + { + } + } +} diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/ModelBinding/Metadata/SpecialBindingSourceMetadataProviderTests.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/ModelBinding/Metadata/SpecialBindingSourceMetadataProviderTests.cs new file mode 100644 index 0000000000..85de204569 --- /dev/null +++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/ModelBinding/Metadata/SpecialBindingSourceMetadataProviderTests.cs @@ -0,0 +1,32 @@ +// 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 Xunit; + +namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata +{ + public class SpecialBindingSourceMetadataProviderTests + { + [Fact] + public void ChecksParameterType_AssignsSpecialBindingSource() + { + // Arrange + var provider = new SpecialBindingSourceMetadataProvider(typeof(Test)); + + var key = ModelMetadataIdentity.ForType( + typeof(Test)); + + var context = new BindingMetadataProviderContext(key, new ModelAttributes(new object[0], new object[0])); + + // Act + provider.CreateBindingMetadata(context); + + // Assert + Assert.Equal(BindingSource.Special, context.BindingMetadata.BindingSource); + } + + private class Test + { + } + } +} From 5e13ef162c771f83ec427d4b3b904714054a6ee2 Mon Sep 17 00:00:00 2001 From: Jass Bagga Date: Tue, 14 Feb 2017 11:45:21 -0800 Subject: [PATCH 5/6] PR feedback and integration tests --- .../Internal/MvcCoreMvcOptionsSetup.cs | 6 +- .../FormFileBindingSourceMetadataProvider.cs | 45 ------ .../SpecialBindingSourceMetadataProvider.cs | 15 +- ...mFileBindingSourceMetadataProviderTests.cs | 32 ---- ...ecialBindingSourceMetadataProviderTests.cs | 7 +- ...ngSourceMetadataProviderIntegrationTest.cs | 152 ++++++++++++++++++ .../MvcOptionsSetupTest.cs | 7 +- 7 files changed, 173 insertions(+), 91 deletions(-) delete mode 100644 src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/Metadata/FormFileBindingSourceMetadataProvider.cs delete mode 100644 test/Microsoft.AspNetCore.Mvc.Core.Test/ModelBinding/Metadata/FormFileBindingSourceMetadataProviderTests.cs create mode 100644 test/Microsoft.AspNetCore.Mvc.IntegrationTests/SpecialBindingSourceMetadataProviderIntegrationTest.cs diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Internal/MvcCoreMvcOptionsSetup.cs b/src/Microsoft.AspNetCore.Mvc.Core/Internal/MvcCoreMvcOptionsSetup.cs index 7f8572157d..7f6ae1c547 100644 --- a/src/Microsoft.AspNetCore.Mvc.Core/Internal/MvcCoreMvcOptionsSetup.cs +++ b/src/Microsoft.AspNetCore.Mvc.Core/Internal/MvcCoreMvcOptionsSetup.cs @@ -79,9 +79,9 @@ public void Configure(MvcOptions options) options.ModelMetadataDetailsProviders.Add(new DefaultBindingMetadataProvider()); options.ModelMetadataDetailsProviders.Add(new DefaultValidationMetadataProvider()); - options.ModelMetadataDetailsProviders.Add(new SpecialBindingSourceMetadataProvider(typeof(CancellationToken))); - options.ModelMetadataDetailsProviders.Add(new FormFileBindingSourceMetadataProvider(typeof(IFormFile))); - options.ModelMetadataDetailsProviders.Add(new FormFileBindingSourceMetadataProvider(typeof(IFormCollection))); + options.ModelMetadataDetailsProviders.Add(new SpecialBindingSourceMetadataProvider(typeof(CancellationToken), BindingSource.Special)); + options.ModelMetadataDetailsProviders.Add(new SpecialBindingSourceMetadataProvider(typeof(IFormFile), BindingSource.FormFile)); + options.ModelMetadataDetailsProviders.Add(new SpecialBindingSourceMetadataProvider(typeof(IFormCollection), BindingSource.FormFile)); // Set up validators options.ModelValidatorProviders.Add(new DefaultModelValidatorProvider()); diff --git a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/Metadata/FormFileBindingSourceMetadataProvider.cs b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/Metadata/FormFileBindingSourceMetadataProvider.cs deleted file mode 100644 index 4d4c9521d1..0000000000 --- a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/Metadata/FormFileBindingSourceMetadataProvider.cs +++ /dev/null @@ -1,45 +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.Reflection; -using Microsoft.AspNetCore.Http; - -namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata -{ - public class FormFileBindingSourceMetadataProvider : IBindingMetadataProvider - { - public Type Type { get; } - - /// - /// Creates a new for the given . - /// - /// - /// The . The provider sets to - /// for properties of and . - /// - public FormFileBindingSourceMetadataProvider(Type type) - { - if (type == null) - { - throw new ArgumentNullException(nameof(type)); - } - - Type = type; - } - - /// - public void CreateBindingMetadata(BindingMetadataProviderContext context) - { - if (context == null) - { - throw new ArgumentNullException(nameof(context)); - } - - if (Type.IsAssignableFrom(context.Key.ModelType)) - { - context.BindingMetadata.BindingSource = BindingSource.FormFile; - } - } - } -} diff --git a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/Metadata/SpecialBindingSourceMetadataProvider.cs b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/Metadata/SpecialBindingSourceMetadataProvider.cs index f329c65478..9e037a859f 100644 --- a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/Metadata/SpecialBindingSourceMetadataProvider.cs +++ b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/Metadata/SpecialBindingSourceMetadataProvider.cs @@ -10,15 +10,19 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata public class SpecialBindingSourceMetadataProvider : IBindingMetadataProvider { public Type Type { get; } + public BindingSource BindingSource { get; } /// - /// Creates a new for the given . + /// Creates a new for the given . /// /// - /// The . The provider sets to - /// for properties of . + /// The . The provider sets of the given or + /// anything assignable to the given . /// - public SpecialBindingSourceMetadataProvider(Type type) + /// + /// The to assign to the given . + /// + public SpecialBindingSourceMetadataProvider(Type type, BindingSource bindingSource) { if (type == null) { @@ -26,6 +30,7 @@ public SpecialBindingSourceMetadataProvider(Type type) } Type = type; + BindingSource = bindingSource; } /// @@ -38,7 +43,7 @@ public void CreateBindingMetadata(BindingMetadataProviderContext context) if (Type.IsAssignableFrom(context.Key.ModelType)) { - context.BindingMetadata.BindingSource = BindingSource.Special; + context.BindingMetadata.BindingSource = BindingSource; } } } diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/ModelBinding/Metadata/FormFileBindingSourceMetadataProviderTests.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/ModelBinding/Metadata/FormFileBindingSourceMetadataProviderTests.cs deleted file mode 100644 index c7fa3fdc49..0000000000 --- a/test/Microsoft.AspNetCore.Mvc.Core.Test/ModelBinding/Metadata/FormFileBindingSourceMetadataProviderTests.cs +++ /dev/null @@ -1,32 +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 Xunit; - -namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata -{ - public class FormFileBindingSourceMetadataProviderTests - { - [Fact] - public void ChecksParameterType_AssignsFormFileBindingSource() - { - // Arrange - var provider = new FormFileBindingSourceMetadataProvider(typeof(Test)); - - var key = ModelMetadataIdentity.ForType( - typeof(Test)); - - var context = new BindingMetadataProviderContext(key, new ModelAttributes(new object[0], new object[0])); - - // Act - provider.CreateBindingMetadata(context); - - // Assert - Assert.Equal(BindingSource.FormFile, context.BindingMetadata.BindingSource); - } - - private class Test - { - } - } -} diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/ModelBinding/Metadata/SpecialBindingSourceMetadataProviderTests.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/ModelBinding/Metadata/SpecialBindingSourceMetadataProviderTests.cs index 85de204569..bf35e2520f 100644 --- a/test/Microsoft.AspNetCore.Mvc.Core.Test/ModelBinding/Metadata/SpecialBindingSourceMetadataProviderTests.cs +++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/ModelBinding/Metadata/SpecialBindingSourceMetadataProviderTests.cs @@ -8,13 +8,12 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata public class SpecialBindingSourceMetadataProviderTests { [Fact] - public void ChecksParameterType_AssignsSpecialBindingSource() + public void CreateBindingMetadata_ForMatchingType_SetsBindingSource() { // Arrange - var provider = new SpecialBindingSourceMetadataProvider(typeof(Test)); + var provider = new SpecialBindingSourceMetadataProvider(typeof(Test), BindingSource.Special); - var key = ModelMetadataIdentity.ForType( - typeof(Test)); + var key = ModelMetadataIdentity.ForType(typeof(Test)); var context = new BindingMetadataProviderContext(key, new ModelAttributes(new object[0], new object[0])); diff --git a/test/Microsoft.AspNetCore.Mvc.IntegrationTests/SpecialBindingSourceMetadataProviderIntegrationTest.cs b/test/Microsoft.AspNetCore.Mvc.IntegrationTests/SpecialBindingSourceMetadataProviderIntegrationTest.cs new file mode 100644 index 0000000000..91f5fba83e --- /dev/null +++ b/test/Microsoft.AspNetCore.Mvc.IntegrationTests/SpecialBindingSourceMetadataProviderIntegrationTest.cs @@ -0,0 +1,152 @@ +// 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.IO; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Internal; +using Microsoft.AspNetCore.Mvc.Abstractions; +using Microsoft.AspNetCore.Mvc.Internal; +using Microsoft.AspNetCore.Mvc.ModelBinding; +using Microsoft.AspNetCore.Mvc.ModelBinding.Binders; +using Microsoft.Extensions.Primitives; +using Xunit; + +namespace Microsoft.AspNetCore.Mvc.IntegrationTests +{ + public class SpecialBindingSourceMetadataProviderIntegrationTest + { + [Fact] + public async Task BindParameter_WithCancellationToken_BindingSourceSpecial() + { + // Arrange + var options = new MvcOptions(); + var setup = new MvcCoreMvcOptionsSetup(new TestHttpRequestStreamReaderFactory()); + + options.ModelBinderProviders.Insert(0, new CancellationTokenModelBinderProvider()); + + setup.Configure(options); + + var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(options); + var parameter = new ParameterDescriptor() + { + Name = "Parameter1", + BindingInfo = new BindingInfo(), + ParameterType = typeof(CancellationTokenBundle), + }; + + var testContext = ModelBindingTestHelper.GetTestContext(request => + { + request.Form = new FormCollection(new Dictionary + { + { "name", new[] { "Fred" } } + }); + }); + + var modelState = testContext.ModelState; + var token = testContext.HttpContext.RequestAborted; + + // Act + var modelBindingResult = await argumentBinder.BindModelAsync(parameter, testContext); + + // Assert + // ModelBindingResult + Assert.True(modelBindingResult.IsModelSet); + + // Model + var boundPerson = Assert.IsType(modelBindingResult.Model); + Assert.NotNull(boundPerson); + Assert.Equal("Fred", boundPerson.Name); + Assert.Equal(token, boundPerson.Token); + + // ModelState + Assert.True(modelState.IsValid); + } + + private class CancellationTokenBundle + { + public string Name { get; set; } + + public CancellationToken Token { get; set; } + } + + [Fact] + public async Task BindParameter_WithFormFile_BindingSourceFormFile() + { + // Arrange + var options = new MvcOptions(); + var setup = new MvcCoreMvcOptionsSetup(new TestHttpRequestStreamReaderFactory()); + + options.ModelBinderProviders.Insert(0, new FormFileModelBinderProvider()); + + setup.Configure(options); + + var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(options); + var parameter = new ParameterDescriptor() + { + Name = "Parameter1", + BindingInfo = new BindingInfo(), + ParameterType = typeof(FormFileBundle), + }; + + var data = "Some Data Is Better Than No Data."; + var testContext = ModelBindingTestHelper.GetTestContext( + request => + { + request.QueryString = QueryString.Create("Name", "Fred"); + UpdateRequest(request, data, "File"); + }); + + var modelState = testContext.ModelState; + + // Act + var modelBindingResult = await argumentBinder.BindModelAsync(parameter, testContext); + + // Assert + // ModelBindingResult + Assert.True(modelBindingResult.IsModelSet); + + // Model + var boundPerson = Assert.IsType(modelBindingResult.Model); + Assert.Equal("Fred", boundPerson.Name); + Assert.Equal("text.txt", boundPerson.File.FileName); + + // ModelState + Assert.True(modelState.IsValid); + } + + private class FormFileBundle + { + public string Name { get; set; } + + public IFormFile File { get; set; } + } + + private void UpdateRequest(HttpRequest request, string data, string name) + { + const string fileName = "text.txt"; + var fileCollection = new FormFileCollection(); + var formCollection = new FormCollection(new Dictionary(), fileCollection); + + request.Form = formCollection; + request.ContentType = "multipart/form-data; boundary=----WebKitFormBoundarymx2fSWqWSd0OxQqq"; + + if (string.IsNullOrEmpty(data) || string.IsNullOrEmpty(name)) + { + // Leave the submission empty. + return; + } + + request.Headers["Content-Disposition"] = $"form-data; name={name}; filename={fileName}"; + + var memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(data)); + fileCollection.Add(new FormFile(memoryStream, 0, data.Length, name, fileName) + { + Headers = request.Headers + }); + } + } +} diff --git a/test/Microsoft.AspNetCore.Mvc.Test/MvcOptionsSetupTest.cs b/test/Microsoft.AspNetCore.Mvc.Test/MvcOptionsSetupTest.cs index a25cbe1586..a0fb09260a 100644 --- a/test/Microsoft.AspNetCore.Mvc.Test/MvcOptionsSetupTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.Test/MvcOptionsSetupTest.cs @@ -165,16 +165,19 @@ public void Setup_SetsUpMetadataDetailsProviders() { var specialParameter = Assert.IsType(provider); Assert.Equal(typeof(CancellationToken), specialParameter.Type); + Assert.Equal(BindingSource.Special, specialParameter.BindingSource); }, provider => { - var formFileParameter = Assert.IsType(provider); + var formFileParameter = Assert.IsType(provider); Assert.Equal(typeof(IFormFile), formFileParameter.Type); + Assert.Equal(BindingSource.FormFile, formFileParameter.BindingSource); }, provider => { - var formCollectionParameter = Assert.IsType(provider); + var formCollectionParameter = Assert.IsType(provider); Assert.Equal(typeof(IFormCollection), formCollectionParameter.Type); + Assert.Equal(BindingSource.FormFile, formCollectionParameter.BindingSource); }, provider => { From 1b4c30d0c7ad3ba2a9f74ceabc99872b4ce168dd Mon Sep 17 00:00:00 2001 From: Jass Bagga Date: Tue, 14 Feb 2017 12:46:32 -0800 Subject: [PATCH 6/6] PR feedback addressed --- .../Internal/MvcCoreMvcOptionsSetup.cs | 6 +++--- ...aProvider.cs => BindingSourceMetadataProvider.cs} | 12 ++++++------ ...ests.cs => BindingSourceMetadataProviderTests.cs} | 4 ++-- ... BindingSourceMetadataProviderIntegrationTest.cs} | 2 +- .../MvcOptionsSetupTest.cs | 6 +++--- 5 files changed, 15 insertions(+), 15 deletions(-) rename src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/Metadata/{SpecialBindingSourceMetadataProvider.cs => BindingSourceMetadataProvider.cs} (83%) rename test/Microsoft.AspNetCore.Mvc.Core.Test/ModelBinding/Metadata/{SpecialBindingSourceMetadataProviderTests.cs => BindingSourceMetadataProviderTests.cs} (83%) rename test/Microsoft.AspNetCore.Mvc.IntegrationTests/{SpecialBindingSourceMetadataProviderIntegrationTest.cs => BindingSourceMetadataProviderIntegrationTest.cs} (98%) diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Internal/MvcCoreMvcOptionsSetup.cs b/src/Microsoft.AspNetCore.Mvc.Core/Internal/MvcCoreMvcOptionsSetup.cs index 7f6ae1c547..9261c0e6f3 100644 --- a/src/Microsoft.AspNetCore.Mvc.Core/Internal/MvcCoreMvcOptionsSetup.cs +++ b/src/Microsoft.AspNetCore.Mvc.Core/Internal/MvcCoreMvcOptionsSetup.cs @@ -79,9 +79,9 @@ public void Configure(MvcOptions options) options.ModelMetadataDetailsProviders.Add(new DefaultBindingMetadataProvider()); options.ModelMetadataDetailsProviders.Add(new DefaultValidationMetadataProvider()); - options.ModelMetadataDetailsProviders.Add(new SpecialBindingSourceMetadataProvider(typeof(CancellationToken), BindingSource.Special)); - options.ModelMetadataDetailsProviders.Add(new SpecialBindingSourceMetadataProvider(typeof(IFormFile), BindingSource.FormFile)); - options.ModelMetadataDetailsProviders.Add(new SpecialBindingSourceMetadataProvider(typeof(IFormCollection), BindingSource.FormFile)); + options.ModelMetadataDetailsProviders.Add(new BindingSourceMetadataProvider(typeof(CancellationToken), BindingSource.Special)); + options.ModelMetadataDetailsProviders.Add(new BindingSourceMetadataProvider(typeof(IFormFile), BindingSource.FormFile)); + options.ModelMetadataDetailsProviders.Add(new BindingSourceMetadataProvider(typeof(IFormCollection), BindingSource.FormFile)); // Set up validators options.ModelValidatorProviders.Add(new DefaultModelValidatorProvider()); diff --git a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/Metadata/SpecialBindingSourceMetadataProvider.cs b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/Metadata/BindingSourceMetadataProvider.cs similarity index 83% rename from src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/Metadata/SpecialBindingSourceMetadataProvider.cs rename to src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/Metadata/BindingSourceMetadataProvider.cs index 9e037a859f..52f84aed6d 100644 --- a/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/Metadata/SpecialBindingSourceMetadataProvider.cs +++ b/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/Metadata/BindingSourceMetadataProvider.cs @@ -7,13 +7,10 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata { - public class SpecialBindingSourceMetadataProvider : IBindingMetadataProvider + public class BindingSourceMetadataProvider : IBindingMetadataProvider { - public Type Type { get; } - public BindingSource BindingSource { get; } - /// - /// Creates a new for the given . + /// Creates a new for the given . /// /// /// The . The provider sets of the given or @@ -22,7 +19,7 @@ public class SpecialBindingSourceMetadataProvider : IBindingMetadataProvider /// /// The to assign to the given . /// - public SpecialBindingSourceMetadataProvider(Type type, BindingSource bindingSource) + public BindingSourceMetadataProvider(Type type, BindingSource bindingSource) { if (type == null) { @@ -33,6 +30,9 @@ public SpecialBindingSourceMetadataProvider(Type type, BindingSource bindingSour BindingSource = bindingSource; } + public Type Type { get; } + public BindingSource BindingSource { get; } + /// public void CreateBindingMetadata(BindingMetadataProviderContext context) { diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/ModelBinding/Metadata/SpecialBindingSourceMetadataProviderTests.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/ModelBinding/Metadata/BindingSourceMetadataProviderTests.cs similarity index 83% rename from test/Microsoft.AspNetCore.Mvc.Core.Test/ModelBinding/Metadata/SpecialBindingSourceMetadataProviderTests.cs rename to test/Microsoft.AspNetCore.Mvc.Core.Test/ModelBinding/Metadata/BindingSourceMetadataProviderTests.cs index bf35e2520f..0d255fa660 100644 --- a/test/Microsoft.AspNetCore.Mvc.Core.Test/ModelBinding/Metadata/SpecialBindingSourceMetadataProviderTests.cs +++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/ModelBinding/Metadata/BindingSourceMetadataProviderTests.cs @@ -5,13 +5,13 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata { - public class SpecialBindingSourceMetadataProviderTests + public class BindingSourceMetadataProviderTests { [Fact] public void CreateBindingMetadata_ForMatchingType_SetsBindingSource() { // Arrange - var provider = new SpecialBindingSourceMetadataProvider(typeof(Test), BindingSource.Special); + var provider = new BindingSourceMetadataProvider(typeof(Test), BindingSource.Special); var key = ModelMetadataIdentity.ForType(typeof(Test)); diff --git a/test/Microsoft.AspNetCore.Mvc.IntegrationTests/SpecialBindingSourceMetadataProviderIntegrationTest.cs b/test/Microsoft.AspNetCore.Mvc.IntegrationTests/BindingSourceMetadataProviderIntegrationTest.cs similarity index 98% rename from test/Microsoft.AspNetCore.Mvc.IntegrationTests/SpecialBindingSourceMetadataProviderIntegrationTest.cs rename to test/Microsoft.AspNetCore.Mvc.IntegrationTests/BindingSourceMetadataProviderIntegrationTest.cs index 91f5fba83e..62e09f867d 100644 --- a/test/Microsoft.AspNetCore.Mvc.IntegrationTests/SpecialBindingSourceMetadataProviderIntegrationTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.IntegrationTests/BindingSourceMetadataProviderIntegrationTest.cs @@ -17,7 +17,7 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests { - public class SpecialBindingSourceMetadataProviderIntegrationTest + public class BindingSourceMetadataProviderIntegrationTest { [Fact] public async Task BindParameter_WithCancellationToken_BindingSourceSpecial() diff --git a/test/Microsoft.AspNetCore.Mvc.Test/MvcOptionsSetupTest.cs b/test/Microsoft.AspNetCore.Mvc.Test/MvcOptionsSetupTest.cs index a0fb09260a..2b166e10db 100644 --- a/test/Microsoft.AspNetCore.Mvc.Test/MvcOptionsSetupTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.Test/MvcOptionsSetupTest.cs @@ -163,19 +163,19 @@ public void Setup_SetsUpMetadataDetailsProviders() provider => Assert.IsType(provider), provider => { - var specialParameter = Assert.IsType(provider); + var specialParameter = Assert.IsType(provider); Assert.Equal(typeof(CancellationToken), specialParameter.Type); Assert.Equal(BindingSource.Special, specialParameter.BindingSource); }, provider => { - var formFileParameter = Assert.IsType(provider); + var formFileParameter = Assert.IsType(provider); Assert.Equal(typeof(IFormFile), formFileParameter.Type); Assert.Equal(BindingSource.FormFile, formFileParameter.BindingSource); }, provider => { - var formCollectionParameter = Assert.IsType(provider); + var formCollectionParameter = Assert.IsType(provider); Assert.Equal(typeof(IFormCollection), formCollectionParameter.Type); Assert.Equal(BindingSource.FormFile, formCollectionParameter.BindingSource); },