Asp.Net Core Model Binding has a set of attributes that gives us the ability to control from what source we want to receive the binding data. In this post I’m going to go through these attributes and show how and when you can use them.
Default Model Binder Behavior
The default behavior of model binder is that it first looks for data in posted form, if it didn’t find any it looks for data in routing value, and lastly it look for data in query strings. For example if we have a URL such as this Product/Detail/2?id=4
, the number 2 will get binded, which is our route variable, but we can change these priorities by specifying exactly where we want our data to come from, Also these are not the only places that we can receive our data from. Asp.Net Core also gives us the ability to receive data from HTTP headers. In subsequent section I go over these attributes and explain their effects.
FromForm Attribute
This attribute is pretty straight forward, it causes the model binder to only use the incoming data from the submitted form.
public IActionResult Detail([FromForm] ProductViewModel productViewModel) => View(productViewModel);
FromRoute Attribute
This attribute cause the model binder to only bind the data that comes from route data:
public IActionResult Detail([FromRoute] int id) => View();
FromQuery Attribute
This attribute tells the model binder to only receive the data from query string, for example if we make a request with this url Product/Detail/2?id=4, the value 4 gets binded as opposed to the default behavior which causes the value to be 2.
public IActionResult Detail([FromQuery] int id) => View();
FromHeader Attribute
This attribute helps us bind values that comes from HTTP request headers, as you can see in the image below.
But there is some problem that might come up when we want to bind to an incoming request header, and that is some of the headers are hyphenated values such as Accept-Language
for example. In these circumstances we can use the name property of this attribute.
[FromHeader(Name = "Accept-Language")]
FromBody Attribute
This attribute cause the binder to bind the data from request body, for example when we make an AJAX request and send some JSON object as its data, we can use this attribute to bind the data.
$.get("/Product/Detail", { productViewModel: productViewModel }, function (data, textStatus, jqXHR) { },"json" );
public IActionResult Detail([FromBody] ProductViewModel productViewModel) => View(productViewModel);
FromServices Attribute
This attribute bind the specified value to the implementation that we configured in our IOC container.
public IActionResult Detail([FromServices] IPrintable printer) => View();
Bind And BindNever Attribute
BindNever
attribute tells the model binder that it shouldn’t bind the specified attribute. As you can see in the code excerpt below, IsAdminUser
never gets binded. This approach can be called black listing properties, which is not a good practice security wise. We’re better off white listing the attributes that we want to bind with Bind
attribute as you can see in the second code excerpt.
Also It’s useful to say that we can use all of these attributes on properties of our class. We don’t have to use them directly in our action parameter list.
Summary
In this post, I’ve discussed how we can use Asp.Net Core’s model binding related attributes to help us have more control over the sources that we use to bind to our model.
Hey Hamid, I found your blog post through a google search. Do you know if it is possible to get a route to accept data from either Body or Query? I.e. public async Task Post([FromBody][FromQuery]DataModel model)
Sometimes the data will come in as a POST body and sometimes in query.
I tried but only one of them will work, but you can only decorate the action with [FromBody] and use syntax like /api/create-name?Name=Joel for query string, but it doesn’t going to bind to a view model by defining querystring this way, you have to define arguments explicitly, which I don’t think are going to be useful.
Hi Hamid, I am new to .NET and writing an API which i want to pass parameters as query string. I use FromQuery bu it didn’t bind i had a lot of problems and after a lot search i found this post which solves my problem but still i dont understand : Quote from you “This attribute tells the model binder to only receive the data from query string, for example if we make a request with this url Product/Detail/2?id=4, the value 4 gets binded as opposed to the default behavior which causes the value to be 2.”
Why i can’t use the url Product/Detail?id=4 instead of Product/Detail/2?id=4 which is unintuitive is there a way around it.
Many Thanks
thank you hamid : )