diff --git a/src/HybridModelBinding/DefaultHybridModelBinder.cs b/src/HybridModelBinding/DefaultHybridModelBinder.cs index 97b0de3..4e30d62 100755 --- a/src/HybridModelBinding/DefaultHybridModelBinder.cs +++ b/src/HybridModelBinding/DefaultHybridModelBinder.cs @@ -1,4 +1,5 @@ -using Microsoft.AspNetCore.Mvc.Formatters; +using HybridModelBinding.ModelBinding; +using Microsoft.AspNetCore.Mvc.Formatters; using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.ModelBinding.Binders; using System.Collections.Generic; @@ -23,7 +24,8 @@ public DefaultHybridModelBinder( .AddModelBinder(Body, new BodyModelBinder(formatters, readerFactory)) .AddValueProviderFactory(Form, new FormValueProviderFactory()) .AddValueProviderFactory(Route, new RouteValueProviderFactory()) - .AddValueProviderFactory(QueryString, new QueryStringValueProviderFactory()); + .AddValueProviderFactory(QueryString, new QueryStringValueProviderFactory()) + .AddValueProviderFactory(Header, new HeaderValueProviderFactory()); } } } diff --git a/src/HybridModelBinding/DefaultPassthroughHybridModelBinder.cs b/src/HybridModelBinding/DefaultPassthroughHybridModelBinder.cs index 05cee3f..3b36722 100644 --- a/src/HybridModelBinding/DefaultPassthroughHybridModelBinder.cs +++ b/src/HybridModelBinding/DefaultPassthroughHybridModelBinder.cs @@ -1,4 +1,5 @@ -using Microsoft.AspNetCore.Mvc.Formatters; +using HybridModelBinding.ModelBinding; +using Microsoft.AspNetCore.Mvc.Formatters; using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.ModelBinding.Binders; using System.Collections.Generic; @@ -23,7 +24,8 @@ public DefaultPassthroughHybridModelBinder( .AddModelBinder(Body, new BodyModelBinder(formatters, readerFactory)) .AddValueProviderFactory(Form, new FormValueProviderFactory()) .AddValueProviderFactory(Route, new RouteValueProviderFactory()) - .AddValueProviderFactory(QueryString, new QueryStringValueProviderFactory()); + .AddValueProviderFactory(QueryString, new QueryStringValueProviderFactory()) + .AddValueProviderFactory(Header, new HeaderValueProviderFactory()); } } } diff --git a/src/HybridModelBinding/HybridModelBinding.csproj b/src/HybridModelBinding/HybridModelBinding.csproj index 2015462..5cd1069 100644 --- a/src/HybridModelBinding/HybridModelBinding.csproj +++ b/src/HybridModelBinding/HybridModelBinding.csproj @@ -12,7 +12,7 @@ false false true - 0.12.0 + 0.13.0 diff --git a/src/HybridModelBinding/ModelBinding/HeaderValueProvider.cs b/src/HybridModelBinding/ModelBinding/HeaderValueProvider.cs new file mode 100644 index 0000000..6d32935 --- /dev/null +++ b/src/HybridModelBinding/ModelBinding/HeaderValueProvider.cs @@ -0,0 +1,82 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc.Internal; +using Microsoft.AspNetCore.Mvc.ModelBinding; +using System; +using System.Collections.Generic; +using System.Globalization; + +namespace HybridModelBinding.ModelBinding +{ + /// + /// Modified from https://github.com/aspnet/Mvc/blob/8d66f104f7f2ca42ee8b21f75b0e2b3e1abe2e00/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/QueryStringValueProvider.cs. + /// + public class HeaderValueProvider : BindingSourceValueProvider, IEnumerableValueProvider + { + public HeaderValueProvider( + BindingSource bindingSource, + IHeaderDictionary values, + CultureInfo culture) + : base(bindingSource) + { + if (bindingSource == null) + { + throw new ArgumentNullException(nameof(bindingSource)); + } + + this.values = values ?? throw new ArgumentNullException(nameof(values)); + Culture = culture; + } + + public CultureInfo Culture { get; private set; } + + private readonly IHeaderDictionary values; + private PrefixContainer prefixContainer; + + protected PrefixContainer PrefixContainer + { + get + { + if (prefixContainer == null) + { + prefixContainer = new PrefixContainer(values.Keys); + } + + return prefixContainer; + } + } + + public override bool ContainsPrefix(string prefix) + { + return PrefixContainer.ContainsPrefix(prefix); + } + + public virtual IDictionary GetKeysFromPrefix(string prefix) + { + if (prefix == null) + { + throw new ArgumentNullException(nameof(prefix)); + } + + return PrefixContainer.GetKeysFromPrefix(prefix); + } + + public override ValueProviderResult GetValue(string key) + { + if (key == null) + { + throw new ArgumentNullException(nameof(key)); + } + + var values = this.values[key]; + + if (values.Count == 0) + { + return ValueProviderResult.None; + } + else + { + return new ValueProviderResult(values, Culture); + } + } + } +} diff --git a/src/HybridModelBinding/ModelBinding/HeaderValueProviderFactory.cs b/src/HybridModelBinding/ModelBinding/HeaderValueProviderFactory.cs new file mode 100644 index 0000000..2f807ad --- /dev/null +++ b/src/HybridModelBinding/ModelBinding/HeaderValueProviderFactory.cs @@ -0,0 +1,49 @@ +using Microsoft.AspNetCore.Mvc.ModelBinding; +using System; +using System.Globalization; +using System.Threading.Tasks; + +namespace HybridModelBinding.ModelBinding +{ + public class HeaderValueProviderFactory : IValueProviderFactory + { + /// + /// We need to re-create a Header BindingSource since the default ASP.NET MVC version + /// is greedy and that won't work as a ValueProvider. + /// + /// Ref. https://github.com/aspnet/Mvc/blob/8d66f104f7f2ca42ee8b21f75b0e2b3e1abe2e00/src/Microsoft.AspNetCore.Mvc.Abstractions/ModelBinding/BindingSource.cs + /// Ref. https://github.com/aspnet/Mvc/blob/8d66f104f7f2ca42ee8b21f75b0e2b3e1abe2e00/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/BindingSourceValueProvider.cs#L41 + /// ArgumentException: The provided binding source 'Header' is a greedy data source. 'BindingSourceValueProvider' does not support greedy data sources. /// Parameter name: bindingSource + /// + private BindingSource Header = new BindingSource( + BindingSource.Header.Id, + BindingSource.Header.DisplayName, + isGreedy: false, + isFromRequest: BindingSource.Header.IsFromRequest); + + public Task CreateValueProviderAsync(ValueProviderFactoryContext context) + { + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + + var headers = context.ActionContext.HttpContext.Request.Headers; + if (headers != null && headers.Count > 0) + { + var valueProvider = new HeaderValueProvider( + Header, + headers, + CultureInfo.InvariantCulture); + + context.ValueProviders.Add(valueProvider); + } + +#if NET451 + return Task.FromResult(0); +#else + return Task.CompletedTask; +#endif + } + } +} diff --git a/src/HybridModelBinding/Source.cs b/src/HybridModelBinding/Source.cs index bd8cc3f..ecb7da6 100755 --- a/src/HybridModelBinding/Source.cs +++ b/src/HybridModelBinding/Source.cs @@ -4,6 +4,7 @@ public static class Source { public const string Body = nameof(Body); public const string Form = nameof(Form); + public const string Header = nameof(Header); public const string QueryString = nameof(QueryString); public const string Route = nameof(Route); }