HybridModelBinding2 0.18.0-alpha.0.53
dotnet add package HybridModelBinding2 --version 0.18.0-alpha.0.53
NuGet\Install-Package HybridModelBinding2 -Version 0.18.0-alpha.0.53
<PackageReference Include="HybridModelBinding2" Version="0.18.0-alpha.0.53" />
paket add HybridModelBinding2 --version 0.18.0-alpha.0.53
#r "nuget: HybridModelBinding2, 0.18.0-alpha.0.53"
// Install HybridModelBinding2 as a Cake Addin #addin nuget:?package=HybridModelBinding2&version=0.18.0-alpha.0.53&prerelease // Install HybridModelBinding2 as a Cake Tool #tool nuget:?package=HybridModelBinding2&version=0.18.0-alpha.0.53&prerelease
HybridModelBinding
For those who want the utmost flexibility in model binding.
The hybrid approach differs from traditional model binding since you get to work with both IModelBinder
and IValueProvider
. This means your model can first get bound with data from the body of a request, and then get updated with data from route or querystring-attributes (in that order). This has the most benefit for users who prefer to have one-model for their request representations.
Examples
ASP.NET Core 3.1
dotnet add package HybridModelBinding
Startup.cs
// Boilerplate...
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services
.AddMvc()
/**
* This will also register a MVC-convention to auto-apply hybrid-binding behavior when a controller-action has a single parameter.
* There are additional rules that get enforced to make sure the class is instantiable before applying this convention.
*/
.AddHybridModelBinder(options =>
{
/**
* This is optional and overrides internal ordering of how binding gets applied to a model that doesn't have explicit binding-rules.
* Internal ordering is: body => form-values => route-values => querystring-values => header-values
*/
options.FallbackBindingOrder = new[] { Source.QueryString, Source.Route };
});
}
Model
using HybridModelBinding;
[HybridBindClass(defaultBindingOrder: new[] { Source.Header, Source.Body, Source.Form, Source.QueryString, Source.Route })]
public class IndexModel
{
// Binding will result from ordering specified at the class-level.
public int? Age { get; set; }
/// <summary>
/// For this specific source, we want to bind to a header-key of `X-Name`.
/// Additionally, this has higher-precedence when looking for a binding-source.
/// </summary>
[HybridBindProperty(Source.Header, "X-Name")]
/// <summary>
/// These are bound using the source-key of `Name`.
/// </summary>
[HybridBindProperty(new[] { Source.Body, Source.Form, Source.QueryString, Source.Route })]
public string Name { get; set; }
}
HybridBindClass
This attribute is optional. It allows specifying default-ordering when a property is not decorated with HybridBindProperty
. This will override FallbackBindingOrder
.
HybridBindProperty
This attribute is optional. It will override HybridBindClass
and FallbackBindingOrder
. It also allows specifying order. This is implicitly set based on the line-number of the attribute. It can also be explicitly set to avoid confusion:
[HybridBindProperty(Source.Header, "X-Name", order: 5)]
[HybridBindProperty(new[] { Source.Body, Source.Form, Source.QueryString, Source.Route }, order: 10)]
public string Name { get; set; }
Declaring multiple attributes on the same line may cause unpredictable results and should be avoided (unless using explicit ordering):
[HybridBindProperty(Source.Header, "X-Name")][HybridBindProperty(new[] { Source.Body, Source.Form, Source.QueryString, Source.Route })]
public string Name { get; set; }
This way is also valid, but may look odd depending whether you read lists top-bottom or bottom-top. In this case, the Source.Body...
-attribute is given higher priority.
[HybridBindProperty(Source.Header, "X-Name", order: 10)]
[HybridBindProperty(new[] { Source.Body, Source.Form, Source.QueryString, Source.Route }, order: 5)]
public string Name { get; set; }
IHybridBoundModel
Maybe you want to see how a property received its value. If your model implements IHybridBoundModel
, this will give you what you seek. This is optional and not required for everything else to work.
public class IndexModel : IHybridBoundModel
{
public int? Age { get; set; }
[HybridBindProperty(Source.Header, "X-Name")]
[HybridBindProperty(new[] { Source.Body, Source.Form, Source.QueryString, Source.Route })]
public string Name { get; set; }
/**
* Key is the property-name
* Value is the source of the binding
*/
public IDictionary<string, string> HybridBoundProperties { get; } = new Dictionary<string, string>();
}
Controller
[HttpGet]
[Route("{age?}/{name?}")]
public IActionResult Index(IndexModel model)
{ }
/**
* This action needs to declare `[FromHybrid]` since the registered convention won't hook it up to the library.
* Without the parameter-attribute, default .NET behavior will get applied—even if your model is decorated with hybrid-attributes.
*/
[HttpGet]
[Route("{age?}/{name?}")]
public IActionResult IndexAlternate(int age, [FromHybrid]IndexModel model)
{ }
FromHybrid
This attribute is optional if the action only contains one, class-parameter. When used, however, also has the ability to specify default-ordering when a property is not decorated with HybridBindProperty
. This will override FallbackBindingOrder
and HybridBindClass
. This is useful if an action has different requirements than other actions using the same model:
[HttpGet]
[Route("{age?}/{name?}")]
public IActionResult IndexAlternate([FromHybrid(new[] { Source.QueryString, Source.Route })]IndexModel model)
{ }
View
<h3>Age: @(Model.Age.HasValue ? Model.Age.ToString() : "N/A")</h3>
<h3>Name: @(string.IsNullOrEmpty(Model.Name) ? "N/A" : Model.Name)</h3>
Results
Based on the setup above, here is how various URIs will get parsed/rendered:
/
<h3>Age: N/A</h3>
<h3>Name: N/A</h3>
/10
<h3>Age: 10</h3>
<h3>Name: N/A</h3>
/10/Bill
<h3>Age: 10</h3>
<h3>Name: Bill</h3>
/10/Bill?age=20
<h3>Age: 20</h3>
<h3>Name: Bill</h3>
/10/Bill?age=20&name=Boga
<h3>Age: 20</h3>
<h3>Name: Bill</h3>
Example using headers and alternate-naming
curl -X GET \
http://localhost/10/Bill?name=Boga \
-H 'X-Name: Billy'
<h3>Age: 20</h3>
<h3>Name: Billy</h3>
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
.NET Core | netcoreapp3.0 was computed. netcoreapp3.1 is compatible. |
.NET Standard | netstandard2.1 is compatible. |
MonoAndroid | monoandroid was computed. |
MonoMac | monomac was computed. |
MonoTouch | monotouch was computed. |
Tizen | tizen60 was computed. |
Xamarin.iOS | xamarinios was computed. |
Xamarin.Mac | xamarinmac was computed. |
Xamarin.TVOS | xamarintvos was computed. |
Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETCoreApp 3.1
- Newtonsoft.Json (>= 13.0.3)
-
.NETStandard 2.1
- Microsoft.AspNetCore.Http (>= 2.1.34)
- Microsoft.AspNetCore.Mvc.Abstractions (>= 2.1.1)
- Microsoft.AspNetCore.Mvc.Core (>= 2.1.1)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated |
---|---|---|
0.18.0-alpha.0.53 | 37 | 12/24/2024 |