Features of .NET Core
In this article, we will go through for how to model binding in dot net core.
If you look with older version of mvc, model binding process is based on model binder and value providers, the matched binder would have access to the request which will extracted data from request. The DefaultModelBinder class was used along with value providers that extracted data from the query string, form data, route values and even from the parsed JSON body.
In asp.net core, there is single framework which is merged by web api and mvc. you might be wondering how does model binding merge in dot net core. Answer is, There are value providers for route values, query string values and form values. Binders based on value providers can get data from any of these, but won’t be able to get data from other sources like the body for example.
Binding with Attributes
Attributes on action parameters can be used to override the default binding behaviour:
- Override binding behaviour
[BindRequired]
: add model state error if binding fails[BindNever]
: ignore the binding of parameter
- Override binding source
[FromHeader]
: use HTTP header[FromQuery]
: use URL query string[FromRoute]
: use routing values[FromForm]
: use form values (via HTTP POST)[FromServices]
: inject value using dependency injection[FromBody]
: use HTTP request body, based on configured formatter (e.g. JSON, XML). Only one action parameter can have this attribute.
- Supply custom binding
[ModelBinder]
: provide custom model binder
Custom model binder
Here we will see one scenario :
I want to bind a Guid parameter to my ASP.NET MVC Core API:
[FromHeader] Guid id
but it’s always null. If I change the parameter to a string and parse the Guid from the string manually it works, so I think it’s not detecting Guid as a convertable type.
And answer is, basically ASP Core only supports binding header values to strings and collections of strings! (whereas binding from route values, query string and body supports any complex type)
for achieve this we can create custom model binder as below :
public Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext.ModelType != typeof(Guid)) return Task.CompletedTask;
if (!bindingContext.BindingSource.CanAcceptDataFrom(BindingSource.Header)) return Task.CompletedTask;
var headerName = bindingContext.ModelName;
var stringValue = bindingContext.HttpContext.Request.Headers[headerName];
bindingContext.ModelState.SetModelValue(bindingContext.ModelName, stringValue, stringValue);
// Attempt to parse the guid
if (Guid.TryParse(stringValue, out var valueAsGuid)){
bindingContext.Result = ModelBindingResult.Success(valueAsGuid);
}
return Task.CompletedTask;
}
And which would be used like below in controller :
public IActionResult SampleAction([FromHeader(Name="my-guid")][ModelBinder(BinderType=typeof(GuidHeaderModelBinder))]Guid foo)
{
return Json(new { foo });
}