Skip to content

Commit

Permalink
Add Header ValueProvider
Browse files Browse the repository at this point in the history
New behavior mimics existing ASP.NET MVC behavior of QueryString and Form value providers.
Not really sure why this isn't included in that package.
Didn't do thorough testing, but did notice one gotcha if identical header-keys are submitted: the values are combined as a single comma-separated string.

Implements #23
  • Loading branch information
billbogaiv committed Jan 19, 2019
1 parent b17c978 commit abb7410
Show file tree
Hide file tree
Showing 6 changed files with 139 additions and 5 deletions.
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);
}
}
}
}
47 changes: 47 additions & 0 deletions src/HybridModelBinding/ModelBinding/HeaderValueProviderFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
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 value provider.
///
/// Ref. https://github.com/aspnet/Mvc/blob/8d66f104f7f2ca42ee8b21f75b0e2b3e1abe2e00/src/Microsoft.AspNetCore.Mvc.Abstractions/ModelBinding/BindingSource.cs
/// </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
}
}
}
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

0 comments on commit abb7410

Please sign in to comment.