Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Header ValueProvider #24

Merged
merged 1 commit into from
Jan 22, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions src/HybridModelBinding/DefaultHybridModelBinder.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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());
}
}
}
6 changes: 4 additions & 2 deletions src/HybridModelBinding/DefaultPassthroughHybridModelBinder.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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());
}
}
}
2 changes: 1 addition & 1 deletion src/HybridModelBinding/HybridModelBinding.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Version>0.12.0</Version>
<Version>0.13.0</Version>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc.Abstractions" Version="2.1.1" />
Expand Down
82 changes: 82 additions & 0 deletions src/HybridModelBinding/ModelBinding/HeaderValueProvider.cs
Original file line number Diff line number Diff line change
@@ -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
{
/// <summary>
/// Modified from https://github.com/aspnet/Mvc/blob/8d66f104f7f2ca42ee8b21f75b0e2b3e1abe2e00/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/QueryStringValueProvider.cs.
/// </summary>
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<string, string> 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);
}
}
}
}
49 changes: 49 additions & 0 deletions src/HybridModelBinding/ModelBinding/HeaderValueProviderFactory.cs
Original file line number Diff line number Diff line change
@@ -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
{
/// <summary>
/// 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
/// </summary>
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
}
}
}
Expand Down
1 change: 1 addition & 0 deletions src/HybridModelBinding/Source.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down