Value providers are part of the model binding ecosystem, and so if you want a more general overview, please see the first post within this series.
Contents
- Introduction
- What is a value provider?
- How it all works
- Why?
- Creating your own Value Provider
- Summary
Introduction
This is the second installment of blog posts that I will be sharing over the coming weeks that discuss the ASP.NET MVC Core GitHub Repository. Each post will take you through a specific feature that the repository provides. The first post can be found here.
Like in previous posts of this series, I am trying to focus more on the low level mechanisms that make up the repository. I will explain features and their merits as I go along, but with an effort to explain the mechanics at a class level.
Also to note, this blog post is focusing on the dev branch of the main repository. At the time of writing, this was version 2.1.0-preview1 of ASP.NET Core MVC.
In this blog post, I will be focusing on value providers. I will explain what value providers are, and how they are generally used. I will go through how you can create your own value providers, and why you would want to do that.
What is a value provider?
A value provider is part of the model binding functionality. The primary purpose of a value provider is to extract values from an incoming request.
It is the model binders role to take the value provider’s result and bind the results to the arguments of the action as well as the model state.
Once the values are determined by the value providers, the model binder will take these, potentially do some post processing like parsing the value to a decimal in the case of DecimalModelBinder, and pass these as results to the main ControllerBinderDelegateProvider class. This class will, using reflection, assign the values to the appropriate parameters based on naming conventions.
How it all works
This section will describe the architecture of value providers within the ASP.NET Core MVC Repository. This section is not explaining how to use value providers, that can be found later on.
Here is an architecture overview of what is about to be described:
MvcOptions
To extend the functionality of ASP.NET Core, like anything in the framework, the best starting point is with the MvcOptions global class.
Value provider factories are registered globally through the MvcCoreMvcOptionsSetup class:
If you wanted to provide your own value providers, then you need to provide a factory that will give it to you within the main ConfigureServices method:
Here you can see that we are adding a value provider factory at the start of the array. This is so that the value provider is ran first, and has precedence.
When action parameters are binded to, the value providers are ran in the order of the factories added. Therefore, it is important that any custom value provider factories are placed in the order you wish them to evaluate.
Value Provider Factories
When working with form data, a HTTP POST request will send form data within the body of a request. This form data is what form value providers will use to retrieve data with. This can be seen within the FormValueProviderFactory class:
Here you can see that the request is probed for form data. This is just one example of how data is fed into a value provider. In this instance, the value provider is the form value provider.
Another example of a factory for value providers is the QueryStringValueProviderFactory. This, as the name suggests, will create a QueryStringValueProvider. It feeds the values using the following code:
As you can see here, there isn’t any rocket science behind how these value providers are being instantiated. It takes the incoming request, and uses different areas of it to build up these value providers.
These value providers are only added to the ValueProviderFactoryContext if there are any values to be submitted. For example, the FormValueProviderFactory will not provide a FormValueProvider, if the request’s content type is not form based.
The Controller Context
The controller context is created by the ControllerActionInvokerProvider like so:
The variable _valueProviderFactories is a collection that comes from the main MvcOptions list.
The ControllerActionInvokerProvider is registered against the dependency injection system. This is then called from the class MvcRouteHandler, which is one of the main IRouter implementations for routing within ASP.NET Core MVC. Routing is beyond the scope of this blog post, but it’s nice to define the boundaries of the current topic.
The ControllerActionInvoker is then created by the ControllerActionInvokerProvider. This invoker then calls a cached binder delegate, that is created within the ControllerBinderDelegateProvider:
This delegate method will then take the controller context with all the value provider factories on it, and call the composite value provider.
The Composite Value Provider
The composite value provider is where everything is joined together. The composite value provider is used to create the value provider “pipeline”. Parts of code such as the ControllerBinderDelegateProvider - i.e., the piece of code that is cached and ran by the main ControllerActionInvoker - will instantiate the provider like so:
This value provider is then passed into the parameter binder:
It is then the parameter binders job to create the model binder, and pass in the value provider to it. Finally, the model binder will use the value provider to retrieve the values it needs to bind to the action parameters.
So we have a list of value providers, and a model binder that will ask for the value, but how does the model binder choose what value it needs?
The model binder will be passed a model name. The model name is derived from the model metadata. For example, if you had a parameter that needed binding to, let’s call it “userId”, then the model binder will ask each and every value provider for a value to the “userId” parameter.
Due to the model binder using the CompositeValueProvider, it will loop through all value providers until it has a value for “userId”. So it will loop through all value providers, which contain values from any possible part of the request, if applicable-e.g., the query string, form data, header data, and so on. Phew, made it.
Binding Sources
You may have noticed that the FormValueProvider inherits from a class called the BindingSourceValueProvider.
A binding source can be used if you want a specific part of the HTTP request to be used for binding purposes. For example, an action can be decorated with the FromHeader attribute, which signifies that the action parameter should be bound using header’s found in the HTTP request.
This post will not go further into binding sources, as it is a little out of scope. However, you can provide a binding source for a value provider, which will then be used to remove value providers if they do not match against the binding source for the current parameter being bound to.
For more information on binding sources, read the microsoft documentation here.
Why?
So at what point you might ask, should I create my own custom value provider? The answer is when ASP.NET Core MVC can not understand the incoming request-i.e., it doesn’t know where to source the values to bind to your action parameters.
99% of cases will be covered by the defaults set out in the MVC framework. However, possible scenarios could be needing a bespoke way of transporting data within the request body, or a bespoke way of interpreting a query string’s values.
Creating your own Value Provider
As previously discussed, to create your own value provider, you first need to hook it up to the global MvcOptions with the use of a class that implements IValueProviderFactory:
This is done within the configure services method at startup. This, like previously discussed, is inserted into the list of global value provider factories at the start. This is so that if the value provider does come back with a value, it is used, as the default is for the model binders to be lazy-i.e., bind the first value they receive.
Here is a custom implementation of a value provider:
With this piece of code, what we are saying is the “CustomHttpBodyValueProvider” will only be applicable, when the content type is “application/custom-content-type”.
If the content type matches, then the body is read in a greedy fashion-i.e., the entire request body is read up front. Then, based on a custom way of organising the body, we extract metadata out of it. In the example, we have a header, contentLenght, and body elements, all separate by a semi-colon.
These are all then fed into the value provider. The value provider is then added to the context which we are currently evaluating. The custom value provider looks like the following:
As can be seen here, there are multiple abstractions that this value provider implements. We use the built in prefix container to help us out here; the same prefix container the default value providers use in ASP.NET Core.
A prefix is essentially the name of an action parameter. For simple types, a value of a prefix will be the parameter name-e.g. for the following action:
The prefix for the first parameter is header. The name ‘header’ is a special key within the custom value provider we have created. So when it comes to the model binder retrieving the value for this parameter, it will match against the first section of the body-e.g. say the body was the following:
The header value would be ’testheader’, the content length would be ‘13’, and the actual body would be ‘hello this is my body’.
This is the basic of model binding, and to change the parameter name to something else, you would have to provide inline binding attributes.
You can also bind to more complex types-e.g., the following example binds to a class:
When the value provider is called, the ‘ContainsPrefix’ method will be given a value of ’testClass’. This will evaluate to false, as the value provider only contains keys for the ‘header’, ‘contentLength’, and ‘body’ names.
The model binder will then look at the inner properties of the complex type, and as our ‘GetValue’ method allows case-insensitive matches, it will match against this property, and bind the ’testheader’ value to it.
This is just one example of the possibilities of custom value providers. What if you didn’t want to work with JSON. What if you wanted to work with YAML, XML, or your own bespoke formatting? this is all possible through the use of custom value providers.
Summary
Through out this blog post, I have discussed how value providers are utilised within the ASP.NET Core world. I have discussed the general architecture of them, and how they fit into the model binder ecosystem. I have also given an example of my own, to show what is achievable with them.
This is the second instalment of blog posts that are not only explaining how features work, but how the innards of the ASP.NET Core repository works itself.
I hope you learned something through this post, and that it will help you on mastering the ASP.NET Core MVC Repository. Please continue with me on my journey through the GitHub repository, and I’ll see you next time.