CHAPTER 15

image

Binding Simple Data Types

In this chapter, I describe the parameter binding system in depth, showing you different ways to obtain values for simple type parameters and explaining how Web API uses a range of different components to get the data values needed to invoke an action method. The features I describe in this chapter are also used to bind complex types using data from the URL, which I explain in Chapter 16. Table 15-1 summarizes this chapter.

Table 15-1. Chapter Summary

Problem

Solution

Listing

Add a new source of data for binding simple action method parameter types.

Implement a custom value provider and value provider factory.

1–9

Apply a value provider to a single action method argument.

Use the ValueProvider attribute or derive a new attribute from the ModelBindingAttribute or ParameterBindingAttribute class.

10–15

Integrate a custom value provider into the default Web API behavior so that it is used without the need for attributes.

Register the value provider factory with the services collection. Ensure that the custom factory implements the IUriValueProviderFactory interface and that the action method parameter is optional.

16–19

Integrate a custom value provider so that it is used based on the name of the action method parameter.

Define a parameter binding rule.

20

Integrate a custom value provider so that it is used only if there are no values from other providers.

Define a parameter binding that queries all of the value providers that have been registered.

21–24

Preparing the Example Project

I am going to continue working with the ExampleApp project from earlier chapters, but I am going to clean it up from the previous chapter to remove the code that manually obtains data from the HttpRequestMessage object. Listing 15-1 shows the revised BindingsController class.

Listing 15-1. Tidying Up the Code in the BindingsController.cs File

using System.Web.Http;
using ExampleApp.Models;

namespace ExampleApp.Controllers {
    public class BindingsController : ApiController {
        private IRepository repo;

        public BindingsController(IRepository repoArg) {
            repo = repoArg;
        }

        [HttpGet]
        [HttpPost]
        public int SumNumbers(Numbers numbers) {
            return numbers.First + numbers.Second;
        }
    }
}

Image Tip  Remember that you don’t have to create the example project yourself. You can download the source code for every chapter for free from Apress.com.

I have returned to using the Numbers model class as the parameter of the SumNumbers action method. Listing 15-2 shows the corresponding changes to the Bindings.cshtml file that allow the user to change the data values that are used to target the action method.

Listing 15-2. Resetting the Contents of the Bindings.cshtml File

@{ ViewBag.Title = "Bindings"; }

@section Scripts { <script src="~/Scripts/bindings.js"></script> }

<div class="alert alert-success" data-bind="css: { 'alert-danger': gotError }">
    <span data-bind="text: response()"></span>
</div>
<div class="form-group">
    <label>First Number</label>
    <input class="form-control" data-bind="value: viewModel().first" />
</div>
<div class="form-group">
    <label>Second Number</label>
    <input class="form-control" data-bind="value: viewModel().second" />
</div>
<button class="btn btn-primary" data-bind="click: sendRequest">Send Request</button>

Listing 15-3 shows the contents of the bindings.js file, in which I have returned to using a POST request that includes the properties defined by the client-side view model.

Listing 15-3. Resetting the Contents of the bindings.js File

var viewModel = ko.observable({ first: 2, second: 5 });
var response = ko.observable("Ready");
var gotError = ko.observable(false);

var sendRequest = function (requestType) {
    $.ajax("/api/bindings/sumnumbers", {
        type: "POST",
        data: viewModel(),
        success: function (data) {
            gotError(false);
            response("Total: " + data);
        },
        error: function (jqXHR) {
            gotError(true);
            response(jqXHR.status + " (" + jqXHR.statusText + ")");
        }
    });
};

$(document).ready(function () {
    ko.applyBindings();
});

The final preparatory change is to remove the binding rule from the WebApiConfig.cs file, as shown in Listing 15-4, and tidy up the routing configuration statements to remove the ones I no longer require.

Listing 15-4. Resetting the Contents of the WebApiConfig.cs File

using System.Web.Http;
using ExampleApp.Infrastructure;

namespace ExampleApp {
    public static class WebApiConfig {
        public static void Register(HttpConfiguration config) {

            config.DependencyResolver = new NinjectResolver();

            config.MapHttpAttributeRoutes();

            config.Routes.MapHttpRoute(
                name: "Binding Example Route",
                routeTemplate: "api/{controller}/{action}/{first}/{second}"
            );

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );
        }
    }
}

Preparing the Common Code

For the first part of this chapter, I focus on customizing the way that simple data types are bound through the parameter binding process. Web API comes with a complete set of classes that can bind the built-in simple types so that a string or int parameter is matched to a value in the request URL or body, as I demonstrated in Chapter 14.

Rather than duplicate this functionality, I am going to show you how to bind parameters to request headers. This isn’t something that is overwhelmingly useful in a real project, but it provides a suitable foundation for demonstrating the customization techniques available.

I am going to show you how to bind headers in different ways in the sections that follow, and to reduce duplication, I have put the code that processes the headers into its own class file. Listing 15-5 shows the contents of the HeadersMap.cs class file that I added to the Infrastructure folder.

Listing 15-5. The Contents of the HeadersMap.cs File

using System.Collections.Generic;
using System.Linq;
using System.Net.Http.Headers;

namespace ExampleApp.Infrastructure {
    public class HeadersMap {
        private Dictionary<string, string> headersCollection;

        public HeadersMap(HttpHeaders headers) {
            headersCollection = headers.ToDictionary(
                  x => x.Key.ToLower().Replace("-", string.Empty),
                  x => string.Join(",", x.Value));
        }

        public string this[string header] {
             get {
                 string key = header.ToLower();
                 return headersCollection.ContainsKey(key) ?
                     headersCollection[key] : null;
             }
        }

        public bool ContainsHeader(string header) {
            return this[header] != null;
        }
    }
}

The HeadersMap class maintains a dictionary that is populated with header names and values from an HttpHeaders object. To make it easier to use header names as action method parameters, I remove hyphens and convert the names to lowercase so that User-Agent is stored as useragent.

The HttpHeaders class is defined in the System.Net.Http namespace, and I will be obtaining instances through the HttpRequestMessage.Headers property when I start to build the binding code. The HttpHeader class is a collection, and for each header in the request, it maps a string containing the header name to an IEnumerable<string> containing one or more values. The individual headers can be accessed through the methods described in Table 15-2.

Table 15-2. The Methods Defined by the HttpHeaders Class

Name

Description

Add(header, IEnumerable<value>)

Adds the specified header with the enumeration of values to the collection

Add(header, value)

Adds the specified header and value to the collection

Clear()

Removes all the headers from the collection

Contains(header)

Returns true if there is a header with the specified name in the collection

GetValues(header)

Returns an IEnumerable<string> containing the values for the specified header

Remove(header)

Removes the specified header from the collection

Image Note  Some of the methods in Table 15-2 can be used to modify the headers because the HttpHeader class is also used by HttpResponseMessage to define the headers that will be sent in an HTTP response.

I can’t work directly with the HttpHeaders object because I want to allow a parameter such as userAgent to match the User-Agent header, which means I need to extract the header data and process it so that it becomes easier to work with. The HttpHeader class implements the IEnumerable<KeyValuePair<string, IEnumerable<string>>> interface, which can be used to enumerate all of the collected headers. This is the same interface I encountered in Chapter 14 when working with the query string parameters, and I deal with it same way: using the LINQ ToDictionary method to create a data structure that it easier to work with, like this:

...
headersCollection = headers.ToDictionary(
    x => x.Key.ToLower().Replace("-", string.Empty), x => string.Join(",", x.Value));
...

I process each header name so that it will match the format I will use for parameter names and use the string.Join method to concatenate multiple header values into a single string.

Image Tip  I have omitted some detail in this section. In fact, there are two subclasses of the HttpHeaders class—HttpRequestHeaders and HttpResponseHeaders—that provide additional members that make it easy to get and set the set of headers that the HTTP specification allows in request and response messages. I use the HttpRequestHeaders class in the “Creating a Parameter Binding Rule” section later in this chapter, but I prefer to work with the HttpHeaders class directly.

Working with Value Providers and Value Provider Factories

Value providers are responsible for getting a single simple data value. Value providers are given the name of the data item that is required and return its value. The value usually comes from the request, but any source of data can be used including the data model. The name of the data item depends on the context in which the value provider is being used. For parameter binding it will be the name of the action method parameter, and for model binding it will be the name of a property from the class that is being instantiated.

Value provider factories are responsible for creating instances of value providers based on the description of an action method parameter. It is the factory that tends to do most of the work in processing a request to prepare a source of multiple data values—like the headers of a request—and the value provider then returns a single value when a request arrives. Table 15-3 puts value providers and their factories in context.

Table 15-3. Putting Value Providers and Value Provider Factories in Context

Question

Answer

What are they?

Value providers are responsible for providing a value for a single parameter before an action method is processed. A value provider factory is responsible for deciding whether the value provider is able to provide a value and provide an instance of it to Web API.

When should you use it?

Use value providers and value provider factories when you want to bind parameter values from parts of the request other than the URL or the body or from some other data source entirely.

What do you need to know?

Value providers and value provider factories are also used in the model binding process. See Chapters 16 and 17 for details.

Understanding Value Providers and Value Provider Factories

Value providers implement the IValueProvider interface, which is defined in the System.Web.Http.ValueProviders namespace. Listing 15-6 shows the definition of the IValueProvider interface.

Listing 15-6. The IValueProvider Interface

namespace System.Web.Http.ValueProviders {

    public interface IValueProvider {

        bool ContainsPrefix(string prefix);

        ValueProviderResult GetValue(string key);
    }
}

I describe the role of the ContainsPrefix method in Chapters 16 and 17, but for the moment it is the GetValue method that is of interest. This method is called when a value is needed, and the result is expressed using an instance of the ValueProviderResult class, which defines the properties and method shown in Table 15-4.

Table 15-4. The Properties and Method Defined by the ValueProviderResult Class

Name

Description

RawValue

This property is used to store the value obtained by the value provider from the request.

AttemptedValue

This property is initially set to be the same as RawValue but will be used to contain an error message if there is a model validation error. See Chapter 18 for details of Web API model validation.

Culture

Gets the culture for the value. This is used when converting the object returned by the RawValue property and should be set to CultureInfo.InvariantCulture if there are no cultural considerations for a value.

ConvertTo(T)

Attempts to convert the value to the specified type. See Chapter 16 for details.

The RawValue and AttemptedValue properties cause confusion, but you simply set both properties to the value extracted from the request and let Web API change the AttempedValue if there are model validation problems. You can’t set the properties in Table 15-4 directly, but the ValueProviderResult class provides a constructor that accepts arguments for all three properties, which you can see used in Listing 15-8.

Notice that the methods defined by the IValueProvider interface do not provide access to details of the request. This is because instances of IValueProvider are created by ValueProviderFactory classes, which are responsible for giving a value provider access to the context information it requires. Listing 15-7 shows the definition of the abstract ValueProviderFactory class.

Listing 15-7. The Abstract ValueProviderFactory Class

using System.Web.Http.Controllers;

namespace System.Web.Http.ValueProviders {

    public abstract class ValueProviderFactory {
        public abstract IValueProvider GetValueProvider(HttpActionContext context);
    }
}

Image Caution  Don’t be tempted to use value providers used to perform data operations. As an example, for the SumNumbers action method in the BindingsController, a value provider might locate the first and second values in the request and add them together to provide a sum argument to the action method. This breaks the separation of concerns that helps make applications easy to understand and maintain. Use value providers only to—as their name suggests—provide data values and leave the operations where they belong.

The ValueProviderFactory class defines a single abstract method called GetValueProvider, which is called when a value is required for an action method parameter. An HttpActionContext object is passed to the GetValueProvider, which allows classes derived from ValueProviderFactory to inspect the request and decide whether the IValueProvider implementations for which they are responsible for may be able to provide values for the request. (Don’t worry if this doesn’t make immediate sense; I show you how to create a custom factory and provider shortly.) Table 15-5 describes the properties defined by the HttpActionContext class.

Table 15-5. The Properties Defined by the HttpActionContext Class

Name

Description

ActionArguments

Returns a Dictionary<string, object> that maps the names of the action method arguments to their types.

ActionDescriptor

Returns an HttpActionDescriptor object that describes the action method that is going to be invoked. See Chapter 22.

ControllerContext

Returns an HttpControllerContext object that describes the controller in which the action method is defined. See Chapter 19 for details of this class.

ModelState

Returns a ModelStateDictionary object used in the model validation process, which I describe in Chapter 18.

Request

Returns the HttpRequestMessage object that describes the current request.

RequestContext

Returns the HttpRequestContext object that provides supplementary information about the request.

Response

Returns the HttpResponseMessage object that will be used to produce the response to the client.

The HttpActionContext class provides a lot of context information, but most ValueProviderFactory implementations will either always create and return a value provider or do so based on some aspect of the HttpRequestMessage object, such as the HTTP verb that has been used for the request.

Creating a Custom Value Provider and Factory

The GetValue method defined by a value provider can be called multiple times to obtain values for different parameters, and this means it is sensible to perform any parsing of request data in the value provider constructor inside the GetValueProvider method of the factory class. To demonstrate, I added a class file called HeaderValueProvider.cs to the Infrastructure folder and used it to define the value provider shown in Listing 15-8.

Listing 15-8. The Contents of the HeaderValueProvider.cs File

using System.Globalization;
using System.Web.Http.ValueProviders;

namespace ExampleApp.Infrastructure {
    public class HeaderValueProvider : IValueProvider {
        private HeadersMap headers;

        public HeaderValueProvider(HeadersMap map) {
            headers = map;
        }

        public ValueProviderResult GetValue(string key) {
            string value = headers[key];
            return value == null
                ? null
                : new ValueProviderResult(value, value, CultureInfo.InvariantCulture);
        }

        public bool ContainsPrefix(string prefix) {
            return false;
        }
    }
}

Image Caution  The Web API interfaces and classes have the same names as those defined by the MVC framework. Both frameworks have an IValueProvider interface and a ValueProviderFactory class (and if you take a look at the sources, you will see that there is a lot of common code behind the scenes). When using Visual Studio to resolve class names, it is easy to select the wrong namespaces and end up with a class that won’t compile. The Web API value provider types are defined in the System.Web.Http.ValueProviders namespace.

The HeaderValueProvider class defines a constructor that receives an instance of the HeadersMap class, and the GetValue method checks to see whether the HeadersMap contains a header that matches the name of the property. If it has, it creates and returns an instance of the ValueProviderResult class that contains the header value.

The GetValue method is not required to return a value, and I return null if there is no corresponding header, indicating that the data that the value provider represents can’t be used to bind to the parameter. Web API will generally use multiple value provider factories and value providers when trying to perform parameter binding, and when one value provider returns null, Web API moves on to the next one and tries again.

To define the value provider factory, I added a class file called HeaderValueProviderFactory.cs to the Infrastructure folder and used it to define the class shown in Listing 15-9.

Listing 15-9. The Contents of the HeaderValueProviderFactory.cs File

using System.Net.Http;
using System.Web.Http.Controllers;
using System.Web.Http.ValueProviders;

namespace ExampleApp.Infrastructure {

    public class HeaderValueProviderFactory : ValueProviderFactory {
        public override IValueProvider GetValueProvider(HttpActionContext context) {
            if (context.Request.Method == HttpMethod.Post) {
                return new HeaderValueProvider(new HeadersMap(context.Request.Headers));
            } else {
                return null;
            }
        }
    }
}

When you start working with value providers, it is easy to believe that the factory is a passive participant in the binding process, but that isn’t the case. The job of the factory is to examine the HttpActionContext object and decide whether the value provider it is responsible for can be used to bind values for the current request.

That doesn’t mean the factory has to figure out whether the value provider will be able to produce a value for a specific parameter—just whether this is a request that the provider will be able to work with at all. This allows a value provider factory to return different value providers to cope with a range of request types or to decide not to create a value provider at all.

To demonstrate how this works, I made the GetValueProvider method a little more complex than it needed to be in the HeaderValueProviderFactory class. I use the Request.Method property defined by the HttpActionContext class to determine whether the request has been made using the POST verb. If it has, then I create and return an instance of the HeaderValueProvider. If not, I return null, indicating that the factory is unwilling to contribute a value provider to bind parameters for this request. When this happens, Web API will try to bind through another value provider factory or, if none are available, generate a binding error.

Applying a Custom Value Provider and Factory

Creating a custom value provider and factory is only part of the process; you must also apply it so that Web API uses it to obtain parameter values. There are several different ways of configuring the way that the value provider and its factory are used, which I describe in the following sections. Some of the techniques I describe are less useful and convenient than others, but they allow me to demonstrate how some of the most important Web API components fit together and how you can customize or replace their behavior.

Understanding How Web API Looks for Values

When Web API needs a value for a simple type parameter, it tries to find one in three different ways. In the sections that follow, I’ll show you each of them and demonstrate how they can be used to set up the value provider factory so that values for parameters are obtained from the value provider.

First, Web API checks to see whether a model binding attribute has been applied directly to the attribute. You saw an example of this in Chapter 14 when I used the FromBody attribute to direct Web API to find a value in the request body.

If there is no such attribute, Web API looks for a parameter binding rule. I demonstrated these in Chapter 14 as well when I showed you how to apply an attribute across the application. That was a basic rule; as you will learn, the binding rules system can do a lot more.

Finally, if there is no directly applied attribute and no parameter binding rule, then Web API acts as though the parameter has been decorated with the FromUri attribute. This is the default behavior and means values are obtained from the request routing or query string if an alternative source for values hasn’t been specified.

The source of a value for a specific parameter can be worked out statically during the configuration stage of the application. Web API can look at the parameter to see whether there is an attribute, check the set of parameter binding rules to see whether there is one for a specific parameter, or decide to use the default behavior, all before the application starts processing requests.

Working out how specific parameters will be bound during the configuration stage allows values to be obtained faster when processing requests because the analysis has already been performed and the results cached, avoiding the need to perform reflection on action methods every time one is invoked.

Caching binding information requires Web API to define a class that describes what the source of a value will be for each parameter, and that is the job of the HttpParameterBinding class, which is defined in the System.Web.Http.Controllers namespace and which defines the properties and methods described in Table 15-6.

Table 15-6. The Properties and Method Defined by the HttpParameterBinding Class

Name

Description

Descriptor

Returns the HttpParameterDescriptor object associated with this binding (and which is passed to the constructor).

ErrorMessage

Returns a string that is used as an error message if the binding fails. If not overridden, this property will return null.

IsValid

Returns true if the binding was successful. If not overridden, this property returns true if the ErrorMessage property returns null.

WillReadBody

Returns true if the value for the parameter will be read from the request body. This property is used to detect when more than one parameter value is going to be read from the body so that an error can be reported (as demonstrated in Chapter 14).

ExecuteBindingAsync(metadata,context, cancelToken)

This method is called to perform the binding and get a value for the parameter. See the following text for details.

SetValue(context, value)

This protected method is used to set the parameter value. The arguments are the HttpActionContext object passed to the ExecuteBindingAsyncMethod and the parameter value.

The HttpParameterBinding class is abstract and is derived to provide binding implementation classes that override the ExecuteBindingAsync method to provide values from different data sources, including the request URL and body.

Don’t worry too much about the HttpParameterBinding class at the moment, other than to keep in mind that each of the techniques that I show you in the sections that follow produces an instance of the HttpParameterBinding class that Web API will cache and then use when it needs a value for a parameter. I show you how to create a custom HttpParameterBinding implementation in the “Creating a Custom Attribute Based on the ParameterBindingAttribute Class” section.

Applying a Value Provider Factory with an Attribute

The first place that Web API looks when it needs a value is at the attributes that have been applied to the parameter in the action method. In particular, Web API looks for attributes that are derived from the abstract ParameterBindingAttribute class, which is defined in the System.Web.Http.Controllers namespace. Listing 15-10 shows the definition of the ParameterBindingAttribute class.

Listing 15-10. The Definition of the ParameterBindingAttribute Class

using System.Web.Http.Controllers;

namespace System.Web.Http

    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Parameter,
        Inherited = true, AllowMultiple = false)]
    public abstract class ParameterBindingAttribute : Attribute {

        public abstract HttpParameterBinding GetBinding(HttpParameterDescriptor
            parameter);
    }
}

The definition of the attribute is simple once you know that Web API is trying to find an HttpParameterBinding object for each parameter. The ParameterBindingAttribute defines an abstract GetBinding method, which takes an HttpParameterDescriptor object and returns an HttpParameterBinding that can be cached and then used when Web API handles a request that targets the action method that defines the parameter.

The HttpParameterDescriptor class is used to describe the parameter for which Web API is looking for a binding. It defines the properties shown in Table 15-7. Some of these properties are used only when binding and validating complex types, which I describe in Chapter 18.

Table 15-7. The Properties Defined by the HttpParameterDescriptor Class

Name

Description

ActionName

Returns the name of the action method.

Configuration

Returns the HttpConfiguration object.

DefaultValue

Returns the default value for the parameter type.

IsOptional

Returns true if the parameter is optional. (See the “Extending the Default Behavior” section for an interesting aspect of using optional parameters.)

ParameterBindingAttribute

Returns the attribute, if any, applied to the parameter to control binding.

ParameterName

Returns the name of the parameter.

ParameterType

Returns the type of the parameter.

Prefix

Returns the prefix of the parameter. I explain prefixes in Chapters 16 and 17.

The job of the ParameterBindingAttribute.GetBinding method is to process an HttpParameterDescriptor object that describes a parameter and produce an HttpParameterBinding object that will be able to produce a value for that parameter at runtime. This pattern—producing an HttpParameterBinding in exchange for an HttpParameterDescriptor—recurs through this part of the chapter because it is the fundamental mechanism that Web API uses to handle parameter binding.

Using the Built-in Parameter Binding Attribute

Web API includes a ValueProvider attribute that can be applied to attributes so that values are obtained through a value provider factory. In Listing 15-11, you can see how I have updated the SumNumbers action method in the Bindings controller to define a new parameter and used the ValueProvider attribute to tell Web API that the value should be obtained from the value provider factory that I defined in the previous section.

Listing 15-11. Adding a Parameter Bound by a Value Provider Factory in the BindingsController.cs File

using System.Web.Http;
using ExampleApp.Models;
using System.Web.Http.ValueProviders;
using ExampleApp.Infrastructure;

namespace ExampleApp.Controllers {
    public class BindingsController : ApiController {
        private IRepository repo;

        public BindingsController(IRepository repoArg) {
            repo = repoArg;
        }

        [HttpGet]
        [HttpPost]
        public string SumNumbers(Numbers numbers,
                [ValueProvider(typeof(HeaderValueProviderFactory))] string accept) {
            return string.Format("{0} (Accept: {1})",
                numbers.First + numbers.Second, accept);
        }
    }
}

I have defined a new parameter called accept, to which I have applied the ValueProvider attribute. In addition to defining the new parameter and applying the attribute, I have changed the return type of the method to string so that I can include the value of the accept parameter in the response sent to the client.

The accept parameter is a simple type for which the default behavior would not be able to find a value without the ValueProvider attribute. (Without the attribute, the default behavior would be to act as though the FromUri attribute had been applied and try to find a value for accept in the routing data or query string.)

To override the default behavior, I have used the ValueProvider attribute, which takes an argument that specifies the type of the value provider factory that should be used to get a value for the parameter, like this:

...
 [ValueProvider(typeof(HeaderValueProviderFactory))] string accept
...

To test the parameter binding attribute, start the application, navigate to /Home/Bindings using the browser, and click the Send Request button. The data that is displayed includes the Accept header. For jQuery, the Accept header defaults to */*, as shown in Figure 15-1.

9781484200865_Fig15-01.jpg

Figure 15-1. Displaying a value obtained from a request header via parameter binding

Creating a Custom Attribute Based on the ModelBindingAttribute Class

Using the ValueProvider attribute works, but including the name of the value provider factory alongside every parameter leads to code that is hard to read. Fortunately, it is a simple matter to create a custom attribute class that is tailored to a specific value provider factory by deriving from the ModelBinderAttribute class. (It isn’t possible to derive from the ValueProviderAttribute class because it is sealed.)

The ModelBindingAttribute class is derived from ParameterBindingAttribute, and it is used to specify the means by which a complex type parameter is created—a process that I describe in detail in Chapter 16. For the purposes of this chapter, the ModelBindingAttribute class is interesting because it defines a GetValueProviderFactories method that can be overridden to return an enumeration of ValueProviderFactory classes that should be used to obtain a value for a simple type parameter.

Listing 15-12 shows the contents of the FromHeaderAttribute.cs class file that I added to the Infrastructure folder and used to define a custom attribute from the ModelBindingAttribute class.

Listing 15-12. The Contents of the FromHeaderAttribute.cs File

using System.Collections.Generic;
using System.Web.Http;
using System.Web.Http.ModelBinding;
using System.Web.Http.ValueProviders;

namespace ExampleApp.Infrastructure {
    public class FromHeaderAttribute : ModelBinderAttribute {

        public override IEnumerable<ValueProviderFactory>
           GetValueProviderFactories(HttpConfiguration configuration) {
            return new ValueProviderFactory[] { new HeaderValueProviderFactory() };
        }
    }
}

I have overridden the GetValueProviderFactories method so that it returns an instance of the HeaderValueProviderFactory class, and in Listing 15-13 you can see how I have applied the FromHeader attribute to the Bindings controller.

Listing 15-13. Applying a Custom Binding Attribute in the BindingsController.cs File

...
[HttpGet]
[HttpPost]
public string SumNumbers(Numbers numbers, [FromHeader] string accept) {
    return string.Format("{0} (Accept: {1})", numbers.First + numbers.Second, accept);
}
...

My custom attribute allows me to bind parameters to header values without having to specify the type of the value provider factory in the action method signature.

Creating a Custom Attribute Based on the ParameterBindingAttribute Class

Creating a custom attribute using the ModelBindingAttribute is the simplest technique, but I am also going to demonstrate how to create an attribute based on the ParameterBindingAttribute class, without the use of any intermediary classes that have other roles within Web API.

The first step is to derive from the HttpParameterBinding class to create an implementation whose ExecuteBindingAsync method gets its values via my custom value provider factory. Listing 15-14 shows the contents of the HeaderValueParameterBinding.cs class file that I added to the Infrastructure folder.

Listing 15-14. The Contents of the HeaderValueParameterBinding.cs File

using System.Threading;
using System.Threading.Tasks;
using System.Web.Http.Controllers;
using System.Web.Http.Metadata;
using System.Web.Http.ValueProviders;

namespace ExampleApp.Infrastructure {
    public class HeaderValueParameterBinding : HttpParameterBinding {
        private HeaderValueProviderFactory factory;

        public HeaderValueParameterBinding(HttpParameterDescriptor descriptor)
            : base(descriptor) {
            factory = new HeaderValueProviderFactory();
        }

        public override Task ExecuteBindingAsync(ModelMetadataProvider metadataProvider,
                HttpActionContext context, CancellationToken cancellationToken) {

            IValueProvider valueProvider = factory.GetValueProvider(context);
            if (valueProvider != null) {
                ValueProviderResult result
                    = valueProvider.GetValue(Descriptor.ParameterName);
                if (result != null) {
                    SetValue(context, result.RawValue);
                }
            }
            return Task.FromResult<object>(null);
        }
    }
}

Remember that the goal of a parameter binding is to call the SetValue method to provide the value that will be used for the parameter when its action method is invoked. The ExecuteBindingAsync method is asynchronous, but all of the classes that I rely on are synchronous, so I satisfy the return type of the method by using the Task.FromResult method, which returns a Task that completes immediately, like this:

...
return Task.FromResult<object>(null);
...

This technique is perfectly acceptable for short, simple methods where the cost of creating and starting a Task is likely to require more work and time than performing the work synchronously.

Now that I have a custom derivation of the HttpParameterBinding class, I can update the custom binding attribute, as shown in Listing 15-15.

Listing 15-15. Deriving from the ParameterBindingAttribute Class in the FromHeaderAttribute.cs File

using System.Collections.Generic;
using System.Web.Http;
using System.Web.Http.ModelBinding;
using System.Web.Http.ValueProviders;
using System.Web.Http.Controllers;

namespace ExampleApp.Infrastructure {
    public class FromHeaderAttribute : ParameterBindingAttribute {
        public override HttpParameterBinding GetBinding(HttpParameterDescriptor param) {
            return new HeaderValueParameterBinding(param);
        }
    }
}

The FromHeaderAttribute class directly follows the pattern I described earlier: processing an HttpParameterDescriptor object in order to create an HttpParameterBinding object that Web API will cache and use to get values for a parameter when requests target its action method.

Extending the Default Behavior

I am going to jump ahead to the third place that Web API looks for a value during parameter binding. If there is no directly applied parameter binding attribute and no binding rule (which I describe in the “Creating a Parameter Binding Rule” section), then binding proceeds as though the parameter had been decorated with the FromUri attribute, even if it has not.

Image Tip  This applies only to simple type parameters. The default behavior for complex type parameters is to proceed as though the FromBody attribute has been applied. I explain how this works in Chapters 16 and 17.

Listing 15-16 shows the definition of the FromUriAttribute class, tidied up and with some error handling statements removed. You can see that this attribute is derived from ModelBinderAttribute and overrides the GetValueProviderFactories method to produce an enumeration of value provider factories.

Listing 15-16. The Definition of the FromUriAttribute Class

using System.Collections.Generic;
using System.Web.Http.ModelBinding;
using System.Web.Http.ValueProviders;

namespace System.Web.Http {

    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Parameter,
         Inherited = true, AllowMultiple = false)]
    public sealed class FromUriAttribute : ModelBinderAttribute {

        public override IEnumerable<ValueProviderFactory>
               GetValueProviderFactories(HttpConfiguration configuration) {

            foreach (ValueProviderFactory f
                    in base.GetValueProviderFactories(configuration)) {
                if (f is IUriValueProviderFactory) {
                    yield return f;
                }
            }
        }
    }
}

The attribute class is simple, but there are two aspects of it that bear explanation, one of which is a trap for the unwary. First, notice that the FromUriAttribute.GetValueProviderFactories implementation gets its data from the base class implementation of the same method.

...
foreach (ValueProviderFactory f in base.GetValueProviderFactories(configuration)) {
...

This is important because I want to add my value provider factory to the set used by the FromUri attribute so that it becomes part of the default behavior. Here is the implementation of the GetValueProviderFactories method in the ModelBinderAttribute class:

...
public virtual IEnumerable<ValueProviderFactory>
        GetValueProviderFactories(HttpConfiguration configuration) {
    return configuration.Services.GetValueProviderFactories();
}
...

The value provider factories used by the FromUri attribute are obtained from the configuration services collection, which I described in Chapter 9.

The second aspect of the way that the FromUri attribute works is the one to watch out for. Not all of the value provider factory classes are used to locate values.

...
foreach (ValueProviderFactory f in base.GetValueProviderFactories(configuration)) {
    if (f is IUriValueProviderFactory) {
        yield return f;
     }
}
...

Only those value provider factory classes that implement the IUriValueProviderFactory interface are returned from the FromUriAttribute.GetValueProviderFactories method. The IUriValueProviderFactory interface defines no methods, and no error will be reported if you don’t declare the interface in a custom factory class; it just won’t be used to get simple type values as part of the default behavior.

Registering the Value Provider Factory

Knowing how the FromUri attribute works allows me to easily integrate header values into my application. First I have to update the HeaderValueProviderFactory class to implement the IUriValueProviderFactory interface, as shown in Listing 15-17.

Listing 15-17. Implementing IUriValueProviderFactory in the HeaderValueProviderFactory.cs File

using System.Net.Http;
using System.Web.Http.Controllers;
using System.Web.Http.ValueProviders;

namespace ExampleApp.Infrastructure {

    public class HeaderValueProviderFactory : ValueProviderFactory,
            IUriValueProviderFactory {
        public override IValueProvider GetValueProvider(HttpActionContext context) {
            if (context.Request.Method == HttpMethod.Post) {
                return new HeaderValueProvider(new HeadersMap(context.Request.Headers));
            } else {
                return null;
            }
        }
    }
}

Now I can register my value provider factory as part of the services collection, either through the dependency injection system or directly during application configuration. Listing 15-18 shows the changes I made to the WebApiConfig.cs file to register the provider factory.

Listing 15-18. Registering a Value Provider Factory in the WebApiConfig.cs File

using System.Web.Http;
using ExampleApp.Infrastructure;
using System.Web.Http.ValueProviders;

namespace ExampleApp {
    public static class WebApiConfig {
        public static void Register(HttpConfiguration config) {

            config.DependencyResolver = new NinjectResolver();

            config.MapHttpAttributeRoutes();

            config.Routes.MapHttpRoute(
                name: "Binding Example Route",
                routeTemplate: "api/{controller}/{action}/{first}/{second}"
            );

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );

            config.Services.Add(typeof(ValueProviderFactory),
                new HeaderValueProviderFactory());
        }
    }
}

I have used the Add method that I described in Chapter 9 to register an instance of the HeaderValueProviderFactory class.

REGISTERING A FACTORY USING DEPENDENCY INJECTION

You can also register value provider factories through the dependency resolver class, which is asked for instances of the ValueProviderFactory class during application startup. Here is the change that would be required to the NinjectResolver.cs file:

...
private void AddBindings(IKernel kernel) {
    kernel.Bind<IRepository>().To<Repository>().InSingletonScope();
    kernel.Bind<ValueProviderFactory>().To<HeaderValueProviderFactory>();
}
...

You need register the value provider factory only once, either directly as in Listing 15-18 or in the resolver.

Updating the Controller

The final step in extending the default behavior is to update the parameter in the action method signature. There are two required changes: removing the FromHeader attribute that I added in Listing 15-12 and making the parameter optional by assigning a default value, as shown in Listing 15-19.

Listing 15-19. Updating the Action Method Parameter in the BindingsController.cs File

using System.Web.Http;
using ExampleApp.Models;
using System.Web.Http.ValueProviders;
using ExampleApp.Infrastructure;

namespace ExampleApp.Controllers {
    public class BindingsController : ApiController {
        private IRepository repo;

        public BindingsController(IRepository repoArg) {
            repo = repoArg;
        }

        [HttpGet]
        [HttpPost]
        public string SumNumbers(Numbers numbers, string accept = null) {
            return string.Format("{0} (Accept: {1})",
                numbers.First + numbers.Second, accept);
        }
    }
}

I need to remove the attribute so that Web API will fall back to using the default behavior, as explained at the start of this section. The need to make the parameter optional is a little more complicated, and it arises because I am doing something that runs counter to an optimization in the way that Web API selection action methods handle requests.

For each request that it receives, Web API needs to select an action method. I describe the selection process in Chapter 22, but for the purposes of this chapter, the important part is an optimization that Web API uses to reduce the pool of possible candidates. For action methods that have one or more parameters, Web API checks to see that there is a mapping between each parameter name and a value in the combined set of properties obtained from the routing data and query string. Knowing that there are different sources of data—including the request body, which has yet to be read—the optimization checks only the parameters that are the following:

  • Not optional (a default value is not assigned in the parameter definition)
  • Is one of the simple types I listed in Chapter 14
  • Has a binding that will obtain a value from provider factory that implements the IUriValueProviderFactory interface

There are other checks that happen as part of the selection process, but if a parameter meets all three of these conditions, Web API assumes that there must be a value in the query string or routing data in order for the action method to be able to receive the request. This is a problem for my accept header, which meets all three of the conditions but doesn’t get its value from the URL. The effect is that the request sent by the client no longer selects the SumNumbers action method and generates a 404 (Not Found) response.

To get around this problem, I must ensure that my accept parameter doesn’t meet all three conditions. I can’t remove the implementation of the IUriValueProviderFactory interface from the value provider factory because the FromUriAttribute class would ignore the factory as a potential source of parameter values.

I could change the parameter so that it isn’t a simple type, but that would take me into the model binding process, which I describe in Chapters 16 and 17. I want to remain focused on simple type parameters in this chapter.

That leaves the first condition that is checked: whether the parameter is optional. By assigning a default value to the accept parameter, I allow Web API to match the action method to requests that don’t have a routing or query string property called accept, solving the problem.

Creating a Parameter Binding Rule

Parameter binding rules are functions that receive an HttpParameterDescriptor object and return an HttpParameterBinding object if the binding they represent will be able to provide a value for the parameter.

These functions are called while Web API is being configured and before any requests are processed, which means that the decisions the functions make are based on the definition of the parameter without any request context.

In Chapter 14, I showed you how to create a parameter binding rule that had the effect of applying the FromUri or FromBody attribute for a specific type throughout the application. Here is the statement that I added to the WebApiConfig.cs file to create the rule:

...
config.ParameterBindingRules.Insert(0, typeof(Numbers),
    x => x.BindWithAttribute(new FromUriAttribute()));
...

I used an extension method, BindWithAttribute, as shorthand to create an HttpParameterBinding object whose ExecuteBindingAsync method gets its values from the FromUriAttribute class for parameters whose type is Numbers.

Most parameter binding rules are for a specific type, which works nicely for complex type parameters where the effect of a rule will be limited and contained. I can’t use a type-specific rule for my header values because the rule would apply to all string parameters, even those whose values should come from somewhere else. I need to be more specific about the parameters that my rule applies to or ensure that I can provide values for all simple-type parameters. I’ll show you both approaches in the sections that follow.

Relying on the Parameter Name

To identify a parameter that will correspond to a header value, I need to use the properties defined by the HttpParameterDescriptor class, as described in Table 15-7. This is the only source of information that my rule function has and means I need some way of detecting the parameters I am interested in based on their name, type, optionality, or the action method in which they are defined.

The obvious choice is to use the parameter name and compare it to a list of request headers. This is not ideal because it creates a special class of reserved names that can’t be used for action method parameters that are not going to be bound from the headers, but it is an interesting technique that lets me demonstrate how the parameter binding rule system works.

At the start of the chapter, I explained that request headers are represented by the HttpHeaders class and accessed through the HttpRequestMessage.Headers property. In fact, the Headers property returns an HttpRequestHeaders object, which is derived from HttpHeaders and defines convenience properties for the headers that the HTTP specification allows in requests. I find the convenience properties rather frustrating to work with because they return objects that parse the header values, rather than let me work directly with the string values, so I prefer to work with the members defined by the HttpHeaders class directly. However, the HttpRequestHeaders convenience properties are useful to me here because I can treat them as an authoritative list of parameter names for which values should be obtained from the request headers.

In Listing 15-20, you can see the binding parameter rule that I added to the WebApiConfig.cs file. The rule tells Web API to use the HeaderValueParameterBinding class I defined in Listing 15-14 when a property name corresponds to a value HTTP header name.

Listing 15-20. Adding a Parameter Binding Rule to the WebApiConfig.cs File

using System.Web.Http;
using ExampleApp.Infrastructure;
using System.Web.Http.ValueProviders;
using System.Net.Http.Headers;

namespace ExampleApp {
    public static class WebApiConfig {
        public static void Register(HttpConfiguration config) {

            config.DependencyResolver = new NinjectResolver();

            config.MapHttpAttributeRoutes();

            config.Routes.MapHttpRoute(
                name: "Binding Example Route",
                routeTemplate: "api/{controller}/{action}/{first}/{second}"
            );

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );

            config.Services.Add(typeof(ValueProviderFactory),
                new HeaderValueProviderFactory());

            config.ParameterBindingRules.Add(x =>
                typeof(HttpRequestHeaders).GetProperty(x.ParameterName) != null
                    ? new HeaderValueParameterBinding(x)
                    : null);
        }
    }
}

I have expressed the parameter binding rule as a lambda expression. I use standard .NET reflection to see whether the HttpRequestHeaders class has a property that matches the parameter name; if it does, I return an instance of the HeaderValueParameterBinding class.

If there is no matching HTTP header, then I return null, which tells Web API that this binding rule is unable to provide a value for the parameter. The search will continue through any other parameter binding rules that have been defined, and the default behavior described in the previous section will be used if none of the rules provides an HttpParameterBinding object.

Image Tip  Parameters bound using the rule shown in Listing 15-21 must still be optional. I have changed the way that values are located for the parameter, but that has no effect on the optimization I described in the action method selection process.

Handling All Simple Type Values

The problem with the technique in the previous section is that it creates a list of reserved parameter names. It isn’t a huge problem because you can apply the FromUri attribute to parameters that need to get values from the URL that are also header names, but it can cause confusion for the unwary.

I need some way to tell which parameters should have values bound from a request header, allowing me to focus on the HeaderValueParameterBinding class as narrowly as possible.

An alternative approach is to provide values for a wider range of parameters but take responsibility for finding values for them even when there is no corresponding header. The simplest way to do this is to build on the built-in functionality and follow the approach taken by the FromUriAttribute by using all of the value provider factories in the service collection.

To demonstrate how this can be done, I added a MultiFactoryParameterBinding.cs file to the Infrastructure folder and used it to define the class shown in Listing 15-21.

Listing 15-21. The Contents of the MultiFactoryParameterBinding.cs File

using System.Threading;
using System.Threading.Tasks;
using System.Web.Http;
using System.Web.Http.Controllers;
using System.Web.Http.Metadata;
using System.Web.Http.ValueProviders;

namespace ExampleApp.Infrastructure {
    public class MultiFactoryParameterBinding : HttpParameterBinding {

        public MultiFactoryParameterBinding(HttpParameterDescriptor descriptor)
            : base(descriptor) {
                // do nothing
        }

        public override Task ExecuteBindingAsync(ModelMetadataProvider metadataProvider,
                HttpActionContext actionContext, CancellationToken cancellationToken) {

            foreach (ValueProviderFactory factory in
                GlobalConfiguration.Configuration.Services.GetValueProviderFactories()) {

                if (factory is HeaderValueProviderFactory
                        || factory is IUriValueProviderFactory) {
                    IValueProvider provider = factory.GetValueProvider(actionContext);
                    ValueProviderResult result = null;
                    if (provider != null && (result =
                            provider.GetValue(Descriptor.ParameterName)) != null) {
                        SetValue(actionContext, result.RawValue);
                        break;
                    }
                }
            }
            return Task.FromResult<object>(null);
        }
    }
}

Image Tip  This class relies on the order in which the value providers are registered in the services collection. This allows you to control the source of data values, but you must ensure that the built-in factories appear before custom ones if you want to give preference to locating data from the request URL. The built-in factories are registered before custom ones, so you should use the Add method when registering your factory. If you want your factory to have precedence over the built-in classes, then use the Insert method instead.

The ExecuteBindingAsync method gets the set of ValueProviderFactory objects in the services collection and uses a foreach loop to call the GetValueProvider method on each of them to try to get an IValueProvider object and, in turn, get a value for the parameter. This continues until a value is provided, at which point I call the SetValue method and break out of the loop.

Image Tip  The ExecuteBindingAsync method is asynchronous, which is useful if you need to look up a data value from a database or perform a complex calculation. It is, however, overkill if you are simply obtaining a value from the request. Rather than create a Task to get the data value, I perform the work synchronously and call Task.FromResult<object>(null) to create a completed Task that has no result.

This is the same approach taken by the default behavior I described in the previous section, except that I have added explicit support for the HeaderValueProviderFactory class as well as factories that implement the IUriValueProviderFactory interface. By default, there are three value provider factories that may be able to provide a value—my custom factory and the two built-in factories described in Table 15-8.

Table 15-8. The Built-in Value Provider Factory Classes

Name

Description

QueryStringValueProviderFactory

Provides values from the query string.

RouteDataValueProviderFactory

Provides values from the routing data. See Chapters 20 and 21 for details of Web API routing.

The reason that I have added explicit support for the HeaderValueProviderFactory class is so that I can work around the action method selection optimization I described earlier. I had to implement the IUriValueProviderFactory interface in the HeaderValueProviderFactory when I was relying on the default behavior and then make the accept parameter optional so that the action method would match the request—but with explicit support in the parameter binding, I can remove the IUriValueProviderFactory interface, and the accept parameter no longer needs to be optional. Listing 15-22 shows how I revised the HeaderValueProviderFactory class to remove the IUriValueProviderFactory interface.

Listing 15-22. Removing an Interface in the HeaderValueProviderFactory.cs File

using System.Net.Http;
using System.Web.Http.Controllers;
using System.Web.Http.ValueProviders;

namespace ExampleApp.Infrastructure {

    public class HeaderValueProviderFactory : ValueProviderFactory {
        public override IValueProvider GetValueProvider(HttpActionContext context) {
            if (context.Request.Method == HttpMethod.Post) {
                return new HeaderValueProvider(new HeadersMap(context.Request.Headers));
            } else {
                return null;
            }
        }
    }
}

Listing 15-23 shows the change I made to the Bindings controller so the accept parameter defined by the SumNumbers action method is not optional.

Listing 15-23. Changing a Parameter Definition in the BindingsController.cs File

...
[HttpGet]
[HttpPost]
public string SumNumbers(Numbers numbers, string accept) {
    return string.Format("{0} (Accept: {1})", numbers.First + numbers.Second, accept);
}
...

The final step is to create the parameter binding rule in the WebApiConfig.cs file, as shown in Listing 15-24.

Listing 15-24. Defining a Parameter Binding Rule in the WebApiConfig.cs File

using System.Web.Http;
using ExampleApp.Infrastructure;
using System.Web.Http.ValueProviders;
using System.Net.Http.Headers;

namespace ExampleApp {
    public static class WebApiConfig {
        public static void Register(HttpConfiguration config) {

            config.DependencyResolver = new NinjectResolver();

            // ...routing statements omitted for brevity...

            config.Services.Add(typeof(ValueProviderFactory),
                new HeaderValueProviderFactory());

            config.ParameterBindingRules.Add(x => {
                return x.ParameterType.IsPrimitive || x.ParameterType == typeof(string)
                    ? new MultiFactoryParameterBinding(x) :
                    null;
            });
        }
    }
}

I created the parameter binding rule using the version of the Add method that takes a Func<HttpParameterDescriptor, HttpParameterBinding> argument. When using a lambda expression, this means that the HttpParameterDescriptor goes to an HttpParameterBinding instance, but only if the parameter is one that the rule wants to support. I use the HttpParameterDescriptor.ParameterType property to see whether the property is a primitive type or a string and, if so, return an instance of the MultiFactoryParameterBinding class. If the parameter isn’t a type I want to work with, I return null to signal that I don’t want to provide a binding and that Web API should continue checking other rules.

Summary

In this chapter, I showed you how value providers and value provider factories work and how they are used by ASP.NET Web API. I explained the sequence that Web API uses to locate parameter bindings during the application startup process and how the results are used to obtain values for action method parameters when requests arrive, allowing custom value providers and factories to supplement the standard sources of data. In Chapter 16, I explain how value providers form the foundation of the model binding feature, which allows complex types to be bound from data obtained by value providers.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.137.216.175