Customizing ASP.​NET Core Part 08: ModelBinders

Jürgen Gutsch - 17 October, 2018

In the last post about OutputFormatters I wrote about sending data out to the clients in different formats. In this post we are going to do it the other way. This post is about data you get into your Web API from outside. What if you get data in a special format or what if you get data you need to validate in a special way. ModelBinders will help you handling this.

About ModelBinders

ModelBinders are responsible to bind the incoming data to specific action method parameters. It binds the data sent with the request to the parameters. The default binders are able to bind data that are sent via the QueryString or sent within the request body. Within the body the data can be sent in URL format or JSON.

The model binding tries to find the values in the request by the parameter names. The form values, the route data and the query string values are stored as a key-value pair collection and the binding tries to find the parameter name in the keys of the collection.

Preparation of the test project

In this post I'd like to send CSV data to a Web API method. I will reuse the CSV data we created in the last post:

This looks basically like any other action. It accepts a list of persons and returns an anonymous object that contains the number of persons as well as the list of persons. This action is pretty useless, but helps us to debug the ModelBinder using Postman.

In the method BindModelAsync we get the ModelBindingContext with all the information in it we need to get the data and to de-serialize it.

First the context get's checked against null values. After that we set a default argument name to model, if none is specified. If this is done we are able to fetch the value by the name we previously set.

If there's no value, we shouldn't throw an exception in this case. The reason is that maybe the next configured ModelBinder is responsible. If we throw an exception the execution of the current request is broken and the next configured ModelBinder doesn't have the chance to get executed.

With a StringReader we read the value into the CsvReader and de-serialize it to the list of models. We get the type for the de-serialization out of the ModelMetadata property. This contains all the relevant information about the current model.

Using the ModelBinder

The Binder isn't used automatically, because it isn't registered in the dependency injection container and not configured to use within the MVC framework.

The easiest way use this model binder is to use the ModelBinderAttribute on the argument of the action where the model should be bound:

I personally prefer the explicit declaration, because the most custom ModelBinders will be pretty specific to an action or to an specific type and theres no hidden magic in the background.

Testing the ModelBinder

To test it, we need to create a new Request in Postman. I set the request type to POST and put the URL https://localhost:5001/api/persons in the address bar. No I need to add the CSV data in the body of the request. Because it is a URL formatted body, I needed to put the data as persons variable into the body:

Conclusion

This is a good way to transform the input in a way the action really needs. You could also use the ModelBinders to do some custom validation against the database or whatever you need to do before the model get's passed to the action.

To learn more about ModelBinders, you need to have a look into the pretty detailed documentation:

While playing around with the ModelBinderProvider Steve describes in his blog, I stumbled upon InputFormatters. Would this actually be the right way to transform CSV input into objects? I definitely need to learn some more details about the InputFormattersand will use this as 12th topic of this series.