Follow me: Jack Histon's Twitter Share on LinkedIn Share on Google+ RSS Feed

Author avatar

Welcome. I am Jack Histon. My career would not be what it is today without dedication and hard work from software bloggers. My purpose is to give back to that online community.

ASP.NET MVC Core – Repository Overview: Value Providers

Friday, 25 August 2017

Tweet about this on Twitter Share on Facebook Share on LinkedIn Share on Google+ Pin on Pinterest Share on Reddit Share on StumbleUpon

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

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:

ASP.NET MVC Core Repository Value Providers Overview

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:


    options.ValueProviderFactories.Add(new FormValueProviderFactory());
    options.ValueProviderFactories.Add(new RouteValueProviderFactory());
    options.ValueProviderFactories.Add(new QueryStringValueProviderFactory());
    options.ValueProviderFactories.Add(new JQueryFormValueProviderFactory());

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:


    services.Configure(
        options => options.ValueProviderFactories.Insert(0, new CustomValueProviderFactory()));

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:


    var valueProvider = new FormValueProvider(
        BindingSource.Form,
        await request.ReadFormAsync(),
        CultureInfo.CurrentCulture);

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:


    var valueProvider = new QueryStringValueProvider(
        BindingSource.Query,
        context.ActionContext.HttpContext.Request.Query,
        CultureInfo.InvariantCulture);

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:


    var controllerContext = new ControllerContext(context.ActionContext);
    // PERF: These are rarely going to be changed, so let's go copy-on-write.
    controllerContext.ValueProviderFactories = new CopyOnWriteList(_valueProviderFactories);

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:


    private Task BindArgumentsAsync()
    {
        ...

        return _cacheEntry.ControllerBinderDelegate(_controllerContext, _instance, _arguments);
    }

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:


    var valueProvider = await CompositeValueProvider.CreateAsync(controllerContext);

This value provider is then passed into the parameter binder:


    var result = await parameterBinder.BindModelAsync(
        controllerContext,
        bindingInfo.ModelBinder,
        valueProvider,
        parameter,
        bindingInfo.ModelMetadata,
        value: null);

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:


    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc();

        services.Configure(
            options => options.ValueProviderFactories.Insert(0, new CustomHttpBodyValueProviderFactory()));
    }

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:


public class CustomHttpBodyValueProviderFactory : IValueProviderFactory
{
    public Task CreateValueProviderAsync(ValueProviderFactoryContext context)
    {
        if (context == null)
        {
            throw new ArgumentNullException(nameof(context));
        }

        var request = context.ActionContext.HttpContext.Request;
        if (request.ContentType == "application/custom-content-type")
        {
            return AddValueProviderAsync(context);
        }

        return Task.CompletedTask;
    }

    private static async Task AddValueProviderAsync(ValueProviderFactoryContext context)
    {
        var request = context.ActionContext.HttpContext.Request;

        var body = string.Empty;

        request.EnableRewind();

        using (var reader = new StreamReader(request.Body, Encoding.UTF8, true, 1024, true))
        {
            body = await reader.ReadToEndAsync();
        }

        request.Body.Position = 0;

        if (string.IsNullOrWhiteSpace(body))
        {
            return;
        }

        var bodyValues = body.Split(';');
        var header = bodyValues[0];
        var contentLength = bodyValues[1];
        var data = bodyValues[2];

        var valueProvider = new CustomHttpBodyValueProvider(
            BindingSource.Form,
            header,
            contentLength,
            data,
            CultureInfo.CurrentCulture);

        context.ValueProviders.Add(valueProvider);
    }
}

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:


public class CustomHttpBodyValueProvider : BindingSourceValueProvider, IEnumerableValueProvider
{
    private readonly string _header;
    private readonly string _contentLength;
    private readonly string _data;
    private PrefixContainer _prefixContainer;
    private readonly CultureInfo _culture;

    public CustomHttpBodyValueProvider(
        BindingSource bindingSource,
        string header,
        string contentLength,
        string data,
        CultureInfo culture) : base(bindingSource)
    {
        _header = header;
        _contentLength = contentLength;
        _data = data;
        _culture = culture;
    }

    protected PrefixContainer PrefixContainer
    {
        get
        {
            if (_prefixContainer == null)
            {
                _prefixContainer = new PrefixContainer(
                    new List { "header", "contentLength", "body" });
            }

            return _prefixContainer;
        }
    }

    public override bool ContainsPrefix(string prefix)
    {
        return PrefixContainer.ContainsPrefix(prefix.ToLower());
    }

    public IDictionary GetKeysFromPrefix(string prefix)
    {
        return PrefixContainer.GetKeysFromPrefix(prefix);
    }

    public override ValueProviderResult GetValue(string key)
    {
        if (string.Equals(key, "header", System.StringComparison.CurrentCultureIgnoreCase))
        {
            return new ValueProviderResult(new StringValues(_header), _culture);
        }
        else if (string.Equals(key, "contentLength", System.StringComparison.CurrentCultureIgnoreCase))
        {
            return new ValueProviderResult(new StringValues(_contentLength), _culture);
        }
        else if (string.Equals(key, "body", System.StringComparison.CurrentCultureIgnoreCase))
        {
            return new ValueProviderResult(new StringValues(_data), _culture);
        }


        return ValueProviderResult.None;
    }
}

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:


[Route("/api/test")]
public JsonResult BindTest(string header, string contentLength, string body)
{
    return Json(new { Header = header, ContentLength = contentLength, Body = body });
}

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:


testheader;13;hello this is my body

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:


[Route("/api/test2")]
public JsonResult BindTest(TestClass testClass)
{
    return Json(new { Header = testClass.Header, ContentLength = testClass.ContentLength, Body = testClass.Body });
}

public class TestClass
{
    public string Header { get; set; }
    public string ContentLength { get; set; }
    public string Body { get; set; }
}

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.

Share with a friend

Please share this blog post so others can learn from it as well.

Tweet about this on Twitter Share on Facebook Share on LinkedIn Share on Google+ Pin on Pinterest Share on Reddit Share on StumbleUpon

Recent Posts

Archives



© 2017 - Jack Histon - Blog