CHAPTER 14

image

Understanding Parameter and Model Binding

In the MVC framework, model binding is the process used to extract values from the HTTP request to provide values for the arguments needed to invoke action methods. In Web API, there are two processes that do this work: parameter binding and model binding. They work in loosely the same way model binding in the MVC framework works, but they are optimized to improve the performance of request handling for web services—and this means there are some important differences to the approach you are used to using.

In any complex Web API project, you will spend a lot of time dealing with the parameter and model binding processes. There is a lot of detail in how these work, and this is the first in a set of chapters that dig into that detail, explain how everything fits together, and demonstrate how to address common binding problems.

In this chapter, I explain the difference between the parameter and model binding processes and demonstrate how they work by default. In Chapter 15, I dig into the detail of how simple types—such as int and string values—are handled. In Chapter 16 and Chapter 17, I do the same for complex types. Along the way, I describe the different ways in which you can customize the parameter and model binding processes, and at the end of Chapter 17, I demonstrate how you can completely replace them with ones of your own design (although, as I explain, there is little reason to do so). Table 14-1 summarizes this chapter.

Table 14-1. Chapter Summary

Problem

Solution

Listing

Use parameter or model binding to find data values in the request.

Define an action method with simple or complex type arguments.

1–5, 8–13

Find values for simple data types in POST requests.

Ensure that the client includes the values for the parameters in the URL, either in the URL so that the values are accessible through the query string or in routing data.

6–7

Read a complex type value from the request URL.

Apply the FromUri attribute to the parameter.

14, 15

Read a simple type value from the request body.

Apply the FromBody attribute to the parameter.

16–17

Apply the effect of the FromUri or FromBody attribute for all parameters of a given type.

Create a parameter binding rule with the BindWithAttribute extension method.

18–20

Obtain data values directly from the HTTP request without using the parameter and model binding features.

Use the properties and extension methods to access the URL and request body.

21–25

Preparing the Example Project

I am going to continue working with the ExampleApp project I have been developing in previous chapters. I am using the project to get the benefit from some of the existing functionality I defined, such as the shared Razor layout that references all of the JavaScript and CSS files I need, but I won’t be working with the part of the application that deals with the Product model and the Web API Products controller for a while.

Creating the Controller

I need to define a new web service that doesn’t follow the RESTful convention so that I can separate the parameter and model binding processes from other Web API features. I added a class file called BindingsController.cs to the Controllers folder and used it to define the Web API controller shown in Listing 14-1.

Listing 14-1. The Contents of 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(int first, int second) {
            return first + second;
        }
    }
}

The Bindings controller defines an action method called SumNumbers, which takes two int arguments, which are added together to create the result. Since this is a simple—rather than RESTful—web service, I have to apply the HttpGet and HttpPost attributes so that the action method can be targeted by HTTP GET and POST requests. (I explain how these attributes work in Web API and why RESTful web services don’t need to use the attributes in Chapter 22.)

Creating the Client

I need a client to consume the new web service. I started by adding an action method to the Home controller, as shown in Listing 14-2.

Listing 14-2. Adding an Action Method in the HomeController.cs File

using System.Web.Mvc;
using ExampleApp.Models;

namespace ExampleApp.Controllers {
    public class HomeController : Controller {
        IRepository repo;

        public HomeController(IRepository repoImpl) {
            repo = repoImpl;
        }

        // ...other action methods omitted for brevity...

        public ActionResult Bindings() {
            return View();
        }
    }
}

The Bindings action method renders the default Razor view. Listing 14-3 shows the contents of the Bindings.cshtml view file that I created in the Views/Home folder.

Listing 14-3. The Contents of the Bindings.cshtml File in the Views/Home Folder

@{ 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>

The view contains HTML form elements that collect the values to be sent to the web service and a button that triggers a function called sendRequest through a Knockout click binding.

I have included a div element styled with the Bootstrap alert class that will display the results from the request, using a Knockout css binding to change color when an error occurs. I defined the bindings (and the view model they apply to) in the bindings.js JavaScript file, which I added to the Scripts folder. Listing 14-4 shows the contents of the JavaScript file.

Listing 14-4. The Contents of the bindings.js File in the Scripts Folder

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

var sendRequest = function () {
    $.ajax("/api/bindings/sumnumbers", {
        type: "GET",
        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();
});

Image Tip  For variety, I have included only the call to the Knockout applyBindings method in the jQuery ready function. I usually put all jQuery code inside the ready function out of habit, but when working with Knockout, only the applyBindings method cannot be called until the browser has finished processing the HTML document.

The JavaScript file defines the observable items needed for the Knockout bindings shown in Listing 14-3, along with the sendRequest function that will be invoked by the click binding on the button element.

Image Tip  I have to specify the action method as part of the URL, since I did not follow the RESTful convention in the Bindings controller. I explain how this convention is managed through the Web API routing system in Chapters 20 and 21, but for the purposes of this chapter, it is enough to know that I have to use /api/bindings/sumnumbers as the URL for the Ajax request.

Adding a New Route

For some of the examples in this chapter, I need to define a new URL route that will let me target the SumNumbers action method in the Bindings controller with a URL like this one:

http://localhost:29844/api/bindings/sumnumbers/10/12

To enable this, I have added a new route to the WebApiConfig.cs file, as shown in Listing 14-5. I explain how Web API URL routing works in Chapters 20 and 21, but for now you can see from the listing that capturing values from segment variables works just as it does in the MVC framework. In addition to defining the new URL route, I have removed the code from the previous chapter that configured the media type formatters.

Listing 14-5. Tidying Up the WebApiConfig.cs File and Defining a New URL Route

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: "Api with extension",
                routeTemplate: "api/{controller}.{ext}/{id}",
                defaults: new {
                    id = RouteParameter.Optional,
                    ext = RouteParameter.Optional
                }
            );

            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 }
            );
        }
    }
}

Testing the Example Application

To test the new web service, start the application and navigate to the /Home/Bindings URL. Enter two numbers into the input elements and click the Send Request button. jQuery will send an Ajax request that targets the SumNumbers action method in the Bindings controller and will display the result when it arrives, as shown in Figure 14-1.

9781484200865_Fig14-01.jpg

Figure 14-1. Testing the example application

Understanding the Default Binding Behavior

Parameter binding and model binding both extract data from the request so that it can be used as arguments to invoke action methods. The result is that you can define action methods that accept .NET types as parameters and let Web API worry about how to get values for them behind the scenes.

Using parameter and model binding ensures that values for action method parameters are extracted from requests consistently, using code that can be applied throughout an application, but Web API doesn’t make you use either kind of binding in your web services. The alternative is to get the data values you need directly from the HttpRequestMessage object, but this can be awkward, duplicative, and error-prone and prevents you from benefitting from features such as model validation, which I describe in Chapter 18. I show you how to get data values from the request without using binding in the “Manually Obtaining Request Values” section later in this chapter.

Image Note  Strictly speaking, the term parameter describes the definition of a variable that a method or function accepts, and an argument is the value of that variable when the method or function is invoked. In practice, these terms are used interchangeably.

In the sections that follow, I describe the default behavior for parameter and model binding. I explain the relationship between the two binding processes, explain when each is used, and show you the most common cause of binding problems. Table 14-2 puts the default binding behavior in context.

Table 14-2. Putting the Default Binding Behavior in Context

Question

Answer

What is it?

Parameter and model binding locates values for action method parameters from requests, simplifying the process of working with the data sent by a client.

When should you use it?

Bindings are used automatically when you define an action method that has parameters.

What do you need to know?

Parameter binding is used to locate values for simple .NET types but will do so only using the request URL. Model binding is used to create complex .NET types but will do so only using the request body. This is the default behavior—see the “Performing Binding Customizations” section for details of how to change the source of data used for binding.

Image Note  I explain the default binding behavior in some detail, and I focus on the two most common pitfalls that you will encounter. My focus on the common problems may give you the impression that parameter and model binding are of limited use, but that’s not the case. In fact, you can control and customize the way that both processes work to address almost any situation, but doing that effectively requires a solid understanding of how the binding processes work by default and the traps that changing the behavior helps to avoid. I dig into the details of how both processes work in Chapters 15, 16, and 17.

Understanding Parameter Binding

Parameter binding is used when an parameter is a simple type, which means it is a TimeSpan, DateTime, or Guid object or one of the .NET primitive types: string, char, bool, int, uint, byte, sbyte, short, ushort, long, ulong, float, double, and decimal.

By default, parameter binding obtains values only from the request URL, which means there are two sources of data values: the routing segments in the URL that has been requested and the query string. Consider the SumNumbers method in the BindingsController class.

...
[HttpGet]
public int SumNumbers(int first, int second) {
    return first + second;
}
...

The two parameters for the action method are both int values, so Web API will use parameter binding to extract values from the response. There are two different styles of URL that can be used to target the SumNumbers method and specify a value for the first and second arguments, as shown in Table 14-3.

Table 14-3. The URLs That Will Target the Action Method

URL

Description

/api/bindings/sumnumbers/10/12

The values for the arguments are obtained from the URL routing information (which I describe in Chapters 20 and 21).

/api/bindings/sumnumbers?first=10&second=12

The values for the arguments are obtained from the query string.

The first URL in Table 14-3 requires the URL route I defined in Listing 14-5, and the easiest way to test it is to use Postman. Send a GET request to the /api/bindings/sumnumbers/10/12 URL, and the parameter binding process will assign 10 and 12 as the values for the action method arguments, returning a result of 22.

JSON AND XML FORMATTING OF SIMPLE VALUES  

If you use Postman to send a GET request to the /api/bindings/sumnumbers/10/12 URL, you will see that the result returned by the web service is just 22. This result has been through the standard media type formatting process that I described in Chapter 13 and been formatted as JSON. It just looks like the unaltered result from the action method because JSON expresses simple values concisely, with no additional packaging required.

If you request the same URL using Google Chrome, the XML media type formatter will be used because Chrome prefers to receive XML over JSON. Here is the result that you will receive:

<int xmlns="http://schemas.microsoft.com/2003/10/Serialization/">
    22
</int>

The more you work with JSON, you more you will come to see why it has displaced the more verbose XML for web applications: JSON is simpler, more concise, and easier to work with.

The first URL is the format you will be familiar with from MVC applications, where it is good practice to create a URL schema that is easy for users to understand and manipulate directly. Not all users want to enter URLs directly, but those that do can start with a URL like this one:

/api/bindings/sumnumbers/10/12

and with a little experimentation work out that changing the last two URL segments allows calculations to be performed directly.

Supporting simple and editable URLs is important in Web API, too, because it allows users to work directly with the web service, but when you are implementing a browser-based client, it is usually the second kind of URL from Table 14-3 that you will rely on because jQuery makes it simple to translate JavaScript objects and Knockout observables into query strings. Consider the sendRequest method that I defined in the bindings.js file.

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

jQuery takes the object assigned to the data setting property and generates a string that contains the name and value of each property it defines. The viewModel object I used in the bindings.js file is a Knockout observable. To get an object that jQuery can process through the data setting, I call the observable name with parentheses: viewModel(). This returns an object with first and second properties, which is then encoded by jQuery to produce a string like this:

first=10&second=20

jQuery uses the HTTP verb being used in the request to decide how to use the encoded string. For GET requests, the encoded string is used as the URL query string, creating a request URL like this:

/api/bindings/sumnumbers?first=10&second=20

Understanding the Parameter Binding Pitfall

By default, parameter binding will only extract values from the URL, which leads to the most common binding problem: trying to bind simple types from the request body. This problem usually occurs because jQuery adapts the way it uses the string generated from the data setting based on the HTTP verb. For GET requests, the string is appended to the URL as the query string, which matches the way that parameter binding works. For other HTTP verbs, jQuery puts the data string in the request body. To demonstrate the problem, I have changed the HTTP verb used by the client in the sendRequest function, as shown in Listing 14-6.

Listing 14-6. Using a Different HTTP Verb in the bindings.js File

...
var sendRequest = function () {
    $.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 + ")");
        }
    });
};
...

When you submit a request using the code shown in the listing, jQuery sends the request to the /api/bindings/sumnumbers URL without any query string and includes the encoded string in the request body. Here is a snapshot of the request that jQuery sends:

POST http://localhost:29844/api/bindings/sumnumbers HTTP/1.1
Host: localhost:29844
Connection: keep-alive
Content-Length: 16
Accept: */*
Origin: http://localhost:29844
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64) ...(truncated)...
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Referer: http://localhost:29844/Home/Bindings
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-GB,en-US;q=0.8,en;q=0.6
Cookie: __RequestVerificationToken=XuxQqvAa36- ...(truncated)...

first=2&second=5

I have highlighted the data string, which is in the same format used for the query string. But, since parameter binding works only on data contained in the URL, values for the first and second arguments required by the SumNumbers action method won’t be located, producing the response shown in Figure 14-2.

9781484200865_Fig14-02.jpg

Figure 14-2. Parameter binding works only on the request URL

Image Tip  I captured the details of the request using Fiddler, which is an excellent web debugging proxy, available free from www.telerik.com/fiddler. The Google Chrome F12 tools provide details of the request but won’t let you see the raw content.

The web service returns a 404 (Not Found) response because the sole action method defined by the Bindings controller has a signature that can’t be matched to the request. I touch on the process of Web API action method selection in Chapter 19 and describe it in depth in Chapter 22, but for this chapter it is enough to know that the request won’t target the action method if the data required by the parameter binding process is in the request body.

This problem comes up at some point in most complex Web API projects, either because you need to change the verb used for a particular kind of request or because you want to make a POST or DELETE request that targets an action method that receives simple data types. There are two ways to solve this problem. The first is to explicitly add values that correspond to simple data type action method parameters to the query string, rather than allowing jQuery to handle the data for you. Listing 14-7 shows the changes required to use this technique in the sendRequest function.

Listing 14-7. Explicitly Setting the Query String in the bindings.js File

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

I have commented out the data setting and changed the URL to which the request will be sent to include a question mark and the encoded string. I have to encode the string explicitly using the jQuery $.param method, which takes an object as its argument and returns a string suitable for use in the query string (this is the same method that jQuery uses for the data setting).

The second technique is to let jQuery put the data in the body and use model binding to extract the values for the action method arguments, overriding the default behavior. I describe how this works in the “Performing Binding Customizations” section.

Understanding Model Binding

Model binding is the counterpart to parameter binding and is used for complex types—which means it is used for any type not in the list I gave in the previous section. Whereas parameter binding works only on the URL by default, model binding works only on the request body.

Since model binding works only on complex types, I need to add a model class to the example application. Listing 14-8 shows the contents of the BindingModels.cs class file that I added to the Models folder.

Listing 14-8. The Contents of the BindingModels.cs File

namespace ExampleApp.Models {

    public class Numbers {
        public int First { get; set; }
        public int Second { get; set; }
    }
}

The Numbers class I defined has First and Second properties that correspond to the simple type parameters from the previous section. Listing 14-9 shows how I updated the SumNumbers action method to use the Numbers class.

Listing 14-9. Using a Model Class 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 calc) {
            return calc.First + calc.Second;
        }
    }
}

I also updated the sendRequest method in the bindings.js file so that the client sends a request that can be processed by model binding, as shown in Listing 14-10.

Listing 14-10. Sending a Complex Type in the bindings.js File

...
var sendRequest = function () {
    $.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 + ")");
        }
    });
};
...

Image Tip  Notice that this is the same request configuration that caused problems for parameter binding in Listing 14-6, and if you look at the request that jQuery sends to the web service, you will see that it is identical to the one I showed you in the previous section. There are a few basic patterns of HTTP request that you will see throughout web service development.

Model binding doesn’t require the client to have any knowledge about the data type that the action method requires. Instead, all of the work to create an object of the type required by the action method is performed at the web service. In this case, the client sends a request that contains values for first and second properties, like this:

first=2&second=5

Web API looks at the URL routing information and determines that the request is intended to target the SumNumbers action method, which requires a Numbers object. Since Numbers is a complex type, the model binding process is applied to transform the first and second values into a Numbers instance. For such a simple object, the transformation process is simple. You create a new instance of the Numbers class and assign values to the properties it defines, but the model binding process can be used to deal with more complex situations, as I describe in Chapter 16 and 17.

Understanding the Model Binding Pitfall

The obvious pitfall with model binding is that it can create objects only from data in the request body, which is the mirror of the most common parameter binding problem. This means that, by default, you can’t use a complex type parameter to receive data from a GET request, but I’ll show you how to resolve this in the “Performing Binding Customizations” section, albeit with some limitations.

The second pitfall is that model binding can extract only one object from the request body. In the MVC framework, the entire request body is processed and stored in memory before request processing starts. The data in the request is available as a collection of name-value pairs that can be used to create as many objects as you need, even to the extent that different objects can be created from the same data items.

The body of Web API requests isn’t read into memory before the model binding process. Instead, the data is available as a stream, and once a model binder has read the data from the stream, it is no longer available for further use. I show you many different ways of customizing and controlling the model binding process in this chapter and the ones that follow, but there is no neat way to step around the one-object-per-request limit.

This problem usually appears when you need to extend the functionality of an action method. To demonstrate, I have defined a new model class in the BindingModels.cs file, as shown in Listing 14-11.

Listing 14-11. Adding a New Class to the BindingModels.cs File

namespace ExampleApp.Models {

    public class Numbers {
        public int First { get; set; }
        public int Second { get; set; }
    }

    public class Operation {
        public bool Add { get; set; }
        public bool Double { get; set; }
    }
}

In Listing 14-12, I have used the new Operation class to extend the SumNumbers action method.

Listing 14-12. Adding an Action Method Parameter  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 calc, Operation op) {
            int result = op.Add ? calc.First + calc.Second :
                calc.First - calc.Second;
            return op.Double ? result * 2 : result;
        }
    }
}

I have defined a new Operation class and changed the signature of the SumNumbers action method so that it defines Numbers and Operation parameters. I use the Operation class properties to perform different calculations within the action method, but the important part of this change is that calling the action method now requires two complex type arguments.

In Listing 14-13, you can see the corresponding changes that I made to the bindings.js file. The values for the Operation properties don’t affect the model binding process, so I have assigned values to the Knockout observable object without giving the user any way to change those values.

Listing 14-13. Adding Data in the bindings.js File

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

var sendRequest = function () {
    $.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();
});

Although the client will now send all of the data values required to create a Numbers object and an Operation object, the limitation of one object per request body will stop that from happening. Start the application, navigate to the /Home/Bindings URL, and click the Send Request button; you will see the response illustrated by Figure 14-3.

9781484200865_Fig14-03.jpg

Figure 14-3. Sending a request to an action method with two complex type arguments

The values that I added for the Operation object don’t matter because Web API will throw an exception before the SumNumbers method is invoked. I explain how exceptions are handled by Web API in Chapter 25, but if you use the Chrome F12 tools to look at the response sent back by the web service, you will see the following message:

Can't bind multiple parameters ('calc' and 'op') to the request's content.

The only way to resolve this problem with the default binding behavior is to create a single complex type that contains all the data values that the action method requires—in this case, a combination of the numeric values and details of the operation that should be performed on them. This is an awful solution because it undermines the benefit of being able to work with complex types in action methods by adopting classes that are just buckets of the name-value pairs; you might as well work directly from the HttpRequestMessage object.

That said, most Web API action methods don’t take multiple complex type arguments, and the more RESTful the web service, the more likely it is to require a single complex argument. The argument is the data object to be created or modified and all of the other information required to process the request—such as the user’s identity, for example—is handled through request headers that are exposed through different parts of Web API and not received as an action method argument. (I explain how user authentication and authorization are performed in Chapters 23 and 24.)

Performing Binding Customizations

Now that you have seen how parameter and model binding work, I can show you how to customize the binding processes and work around the limitations that I described in the previous section. Binding data values to action method parameters in Web API is flexible and fully featured, but it requires more work than the MVC framework to get control of the process. In the sections that follow, I show you the day-to-day customizations that you use to alter the way that parameter and model binding work. These are the simple customizations that tweak the existing behavior. In Chapters 16 and 17, I show you how to perform advanced customizations that alter the binding processes in more profound ways. Table 14-4 puts the simple binding customizations in context.

Table 14-4. Putting the Simple Binding Customizations in Context

Question

Answer

What are they?

The FromUri and FromBody attributes can be used to override the default parameter and model binding behavior and specify a location for the binding data. A binding rule can be used to create the same effect for all Web API controllers in the application.

When should you use them?

Use these attributes when the default behavior does not match the location of the data in the requests that you receive from clients.

What do you need to know?

There are limitations with both attributes. In particular, the FromBody attribute requires the request body to be in a specific format that contains only one data value. The FromUri attribute is more useful but should be used with caution because it can create a tight coupling between the client and the web service.

Binding Complex Types from the Request URL

The first kind of customization is to override the default behavior for model binding so that values are taken from the request URL rather than the body. This customization is performed by applying the FromUri attribute, defined in the System.Web.Http namespace, to the parameters you created from the data in the URL. Listing 14-14 shows how I have applied the FromUri attribute to the parameters defined by the SumNumbers action method in the Bindings controller.

Listing 14-14. Getting Values for a Complex Type from the Request URL in the BindingsController.cs File

...
[HttpGet]
[HttpPost]
public int SumNumbers([FromUri] Numbers calc, [FromUri] Operation op) {
    int result = op.Add ? calc.First + calc.Second :
        calc.First - calc.Second;
    return op.Double ? result * 2 : result;
}
...

I have applied the FromUri attribute to both parameters, which means that the data extracted from the URL segments by the routing configuration or from the query string will be used to set the properties of the Numbers and Operation objects that will be passed to the SumNumbers method. Listing 14-15 shows the client-side code that includes the data in the query string for a POST request.

Listing 14-15. Making a POST Request with Query String Data in the bindings.js File

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

var sendRequest = function () {
    $.ajax("/api/bindings/sumnumbers", {
        type: "GET",
        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();
});

Image Tip  You may notice that Microsoft uses the term URI in the name of the FromUri attribute but that I use URL. All URLs are URIs, as defined by RFC 3986, and both terms are correct when talking about web applications and web services, but URL is more widely used and understood. Microsoft is being a little pedantic by sticking with the more general term.

You don’t have to use the FromUri attribute on all of the complex type parameters defined by an action method, but it is usually a sign of a problem if you are mixing and matching the locations for the data used for different parameters. See the “Using the FromUri Attribute” sidebar for details.

USING THE FROMURI ATTRIBUTE

For GET requests, the FromUri attribute should be applied to all of the complex data type parameters because the client expects to put its data into the query string, and, as you have seen, this is what jQuery will do automatically.

For other HTTP verbs, the FromUri attribute should not be used at all. The client will put its data into the request body by default, and applying the FromUri attribute to some parameters and not others means that the client has to know where the web service is going to look for different pieces of information, which causes the tight-coupling problem I described in Chapter 4.

One common reason for using the FromUri attribute for non-GET requests is to create objects from the URL that can be validated using the model binding process, which I describe in Chapter 18. The problem with this approach is that the client is then required to differentiate between model errors that relate to the data item contained in the body and model errors that relate to some opaque aspect of the URL.

I see this most frequently for PUT requests, where the modified object is contained in the body but a complex type is pulled from the URL routing data and used to validate the format of the URL and to make sure that the request relates to a data object that exists. The end result is a client that requires detailed knowledge of the web service implementation or that displays validation errors to the user about the structure of the URL, which is just confusing.

Use URL routing (as described in Chapters 20 and 21) to enforce URL structure and use standard HTTP status codes to tell the client when a request can’t be processed (as described in Chapter 11).

Binding Simple Types from the Request Body

The FromBody attribute allows simple types to be obtained from the request body, rather than the URL. The FromBody attribute doesn’t work around the one-object-per-request limit, which means you can get one simple type or one complex type from the request body—and that means you can apply the attribute only to a single action method parameter. If you apply the FromBody attribute more than once or use the attribute in method that also has a complex type parameters, then you will receive a Can't bind multiple parameters error.

The FromBody attribute is almost useless for reading simple types because it is so limited in the way that it reads values from the request body. The body must contain only a single value, and it must be encoded in a particular way. The attribute is more useful when used for complex types, as I describe in Chapter 16 and Chapter 17. In Listing 14-16, you can see that I have revised the SumNumbers action method so that it defines one simple type parameter, decorated with the FromBody attribute.

Listing 14-16. Using the FromBody Attribute in the BindingsController.cs File

...
[HttpGet]
[HttpPost]
public int SumNumbers([FromBody] int number) {
    return number * 2;
}
...

The SumNumbers method has an int parameter called number that is decorated with the FromBody attribute. Listing 14-17 shows the changes I have made to the sendRequest function in the bindings.js file to create a request that will target the new version of the SumNumbers method.

Listing 14-17. Targetting an Action Method with the FromBody Attribute in the bindings.js File

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

I use the data setting to encode an object that has a single property and value. I set the name of the property to the empty string (''), and the value is obtained from the first property of the view model (I am going to ignore the other view model properties for this example).

This awkward hack causes jQuery to create a request body like this, assuming that the first property has a value of 50:

=50

This is the format that the FromBody attribute requires. There can be only one value, it cannot be assigned a name, and it must be prefixed with the equal sign (=). To test the use of the attribute, start the application and navigate to /Home/Bindings in the browser. Enter 50—or any other numeric value that you like—and click the SendRequest button.

When the SumNumbers action method is invoked, the FromBody attribute will have caused the value for the number attribute to be obtained from the body, producing the result shown in Figure 14-4.

9781484200865_Fig14-04.jpg

Figure 14-4. Using the FromBody attribute to get a simple type value from a request body

The FromBody attribute isn’t quite as useless as it appears. The problem is that the default Web API classes that support the attribute are not adept at dealing with this kind of request. I’ll show you how to increase the flexibility of the FromBody attribute in Chapter 17.

Defining a Binding Rule

The FromUri and FromBody attributes let you specify the source of the data for a binding, but they need to be applied to every action method parameter, which is just the sort of thing you can easily forget to do consistently across an application. An alternative is to define a binding rule, which tells Web API how to bind parameters of a specific type throughout an application.

The binding rule system allows for a lot of configuration, and in this chapter I will describe two simple rules that you can create that have the same effect as using the FromUri and FromBody attributes but apply to all of the parameters of a specific type throughout the Web API controllers in an application.

The HttpConfiguration.ParameterBindingRules property returns a collection of parameter binding rules. Binding rules are added to the collection during the configuration stage and then used to figure out how values for parameters are going to be obtained. (I am simplifying this process; I explain it in more detail in Chapter 15.)

When you define a new rule, you need to define a method that receives a description of an action method parameter and returns an object that will be able to bind a value for it. The description is provided by an HttpParameterDescriptor object, and the binding is performed by an HttpParameterBinding object. I describe HttpParameterDescriptor and HttpParameterBinding in detail in Chapter 16, but for the simple rules you can use an extension method defined in the System.Web.Http.Controllers namespace, which operates on an instance of the HttpParameterDescriptor class and creates an HttpParameterBinding object that applies either the FromUri or FromBody attribute throughout the application.

Listing 14-18 shows the use of the extension method to define the binding rule in the WebApiConfig.cs file.

Listing 14-18. Defining a Binding Rule in the WebApiConfig.cs File

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

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

            config.DependencyResolver = new NinjectResolver();

            // ...other configuration statements omitted for brevity...

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

The new statement in the listing creates a binding rule that tells Web API that all Numbers parameters should be treated as though they have the FromUri attribute applied directly. To create a simple binding rule, use the Insert method on the collection returned by the HtppConfiguration.ParameterBindingRules property, like this:

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

The rules in the collection are evaluated in order, and using the Insert method allows control over how binding is performed. Complex binding rules can match parameters using fine-grained detail about the action method and controller that contain them, but for simple rules it is best to insert them at position zero to be sure that no other rules take precedence.

The Insert method takes three arguments: the position in the collection into which the new rule should be inserted, the type to which the rule will apply (Numbers in this case), and a function that takes an HttpParameterDescriptor object and returns an HttpParameterBinding object. The BindWithAttribute extension method sidesteps the need to write the function. It takes an instance of the attribute that you want to apply in the rule and uses it to create an HttpParameterBinding object for you.

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

In this example, I have used an instance of the FromUriAttribute class. The convention in C# is that attributes are implemented by classes whose names combine the name of the attribute and Attribute so that the FromUri attribute is implemented by the FromUriAttribute class and the FromBody attribute is implemented by the FromBodyAttribute class.

Updating the Controller and Client

Having defined a binding rule, I no longer need to apply an attribute directly to the parameter defined by the SumNumbers action method, as shown in Listing 14-19.

Listing 14-19. Removing the Binding Attribute 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 calc) {
            return calc.First + calc.Second;
        }
    }
}

Image Tip  Applying the FromUri or FromBody attribute to a parameter overrides the binding rules. You can use a binding rule to define a default behavior and then change it for specific parameters.

Since my binding rule uses the FromUri attribute, I have updated the sendRequest function in the bindings.js file to make a GET request so that jQuery will use the data object values to create a query string, as shown in Listing 14-20. I have also change the data property so that all of the properties of the viewModel object are included, rather than just the single value I sent in the previous section to satisfy the FromBody attribute.

Listing 14-20. Using a GET Request in the bindings.js File

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

The result is that the Numbers parameter defined by the SumNumbers action method—and any other Numbers parameter defined by an action method in a Web API controller—will be obtained from the URL, either from the query string or from the routing data.

Manually Obtaining Request Values

Now that you have seen how the basic parameter and mode binding features work, I am going to finish this chapter by demonstrating how you can bypass both processes and get the data you need directly from the request data.

My advice is to use the binding features wherever you can, but working directly with the request can be useful if you have multiple generations of clients targeting the same action method with different data—and, potentially, different expectations of what the web service will do with that data.

This isn’t something you should need to do often, not least because it is easy to create a web service that doesn’t get the data it requires or that breaks when some aspect of the request changes. But I want to demonstrate that binding values to parameters is entirely optional and is intended only to make web service development simpler and more natural. If you find yourself getting bogged down by the quirks and oddities of the binding features, it can be helpful to know that there is an alternative approach available.

To prepare for this example, I am going to modify the client I have been using in this chapter so that it sends different requests to the same action method so that I can demonstrate how to work directly with the request data in the Web API action method to work out what kind of request has been received and deal with it appropriately.

In Listing 14-21, you can see the changes that I have made to the Bindings.cshtml file in the Views/Home folder.

Listing 14-21. Extending the Client in the Bindings.cshtm 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>

<button class="btn btn-primary" data-bind="click: sendRequest.bind($data, 'sum')">
    Send Sum Request
</button>
<button class="btn btn-primary" data-bind="click: sendRequest.bind($data, 'difference')">
    Send Difference Request
</button>

I removed the input elements that allow the user to change the view model values and defined two buttons that call the sendRequest method with an argument indicating the kind of request that is required, either sum or difference. You can see how I use the argument to alter the data sent in the request in Listing 14-22, which shows the changes I made to the bindings.js file.

Listing 14-22. Sending Different Request Data in 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: "GET",
        data: requestType == "sum"
            ? viewModel() : {value1: viewModel().first, value2: viewModel().second },
        success: function (data) {
            gotError(false);
            response("Total: " + data);
        },
        error: function (jqXHR) {
            gotError(true);
            response(jqXHR.status + " (" + jqXHR.statusText + ")");
        }
    });
};

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

If the argument received by the sendRequest function is sum, then I send a request that contains properties called first and second. Otherwise, I send a request that contains value1 and value2 properties. The values assigned to these properties are not important—just the fact that different data properties will be sent. In Listing 14-23, you can see how I have updated the SumNumbers action method to receive these requests.

Listing 14-23. Getting Request Data Directly in the BindingsController.cs File

using System.Web.Http;
using ExampleApp.Models;
using System.Linq;
using System.Net.Http;
using System.Collections.Generic;
using System.Net;

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

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

        [HttpGet]
        [HttpPost]
        public IHttpActionResult SumNumbers() {
            Dictionary<string, string> jqData
                = Request.GetQueryNameValuePairs().ToDictionary(x => x.Key,
                    x => x.Value);
            int firstValue, secondValue;
            if (TryGetValues(jqData, "first", "second", out firstValue,
                out secondValue)) {
                    return Ok(firstValue + secondValue);
            } else if (TryGetValues(jqData, "value1", "value2", out firstValue,
                out secondValue)) {
                    return Ok(firstValue - secondValue);
            } else {
                return StatusCode(HttpStatusCode.BadRequest);
            }
        }

        private bool TryGetValues(Dictionary<string, string> data, string key1,
                string key2, out int val1, out int val2) {
            val1 = val2 = 0;
            return data.ContainsKey(key1) && data.ContainsKey(key2)
                && int.TryParse(data[key1], out val1) && int.TryParse(data[key2],
                    out val2);
        }
    }
}

In this example, I am dealing with a GET request that contains the data I require in the query string (I show you how to deal with a POST request in the next section). The easiest way to get the query string data is to use one of the extension methods that Web API adds to the System.Net namespace that operate on the HttpRequestMessage object that represents the request, like this:

...
Dictionary<string, string> jqData
    = Request.GetQueryNameValuePairs().ToDictionary(x => x.Key, x => x.Value);
...

The Request property is defined by the ApiController class and returns the HttpRequestMessage object. The GetQueryNameValuePairs method is an extension that returns a IEnumerable<KeyValuePair<string, string>> object, which is an entirely useless way to present the data unless you want to enumerate it with a foreach loop. I use the LINQ ToDictionary method to process the data and create a Dictionary<string, string>, which maps the query string properties to their values. This gives me a more useful data collection to work with.

Image Tip  You might be concerned that the query string is supposed to be enumerated for performance reasons, rather like the request body (which I demonstrate in the next section). In fact, the use of IEnumerable is just poor design because the data objects have already been read into memory and stored in an array in the GetQueryNameValuePairs method anyway.

When dealing with the request data directly, you take responsibility for ensuring that the values you require are part of the request and can be parsed into the required data type. I defined the TryGetValues method to check that pairs of properties are contained in the request and can be parsed into int values. (This relies on the use of the out keyword, which I always regard as an indicator of gnarly, twisted code in web services and keep an eye out for when taking over a project.)

Within the SumNumbers method itself, I try to get the pairs of data properties that define the different requests and perform operations on them. I have changed the result to IHttpActionResult, which I described in Chapter 11 and which allows me to use the Ok method to return data to the client for successful requests and use the StatusCode method to return 400 (Bad Request) responses when the request doesn’t contain the data I am expecting.

To test the changes, start the application, navigate the browser to /Home/Bindings, and click each button in turn. As shown in Figure 14-5, clicking the Send Sum Request button will generate a response of 7 (having added 2 and 5 together), while clicking the Send Difference Request button will generate a request of -3 (having subtracted 5 from 2).

9781484200865_Fig14-05.jpg

Figure 14-5. Obtaining data directly from the request

Handling POST Requests

The key to working with the request body is the HttpContent class, which is defined in the System.Net.Http namespace. An instance of the HttpContent class is returned by the HttpRequestMessage.Content property and can be used to determine the nature of the content and access it. The methods and properties of the HttpContent class are supplemented by Web API extension methods that make it easier to work with different types of request. Table 14-5 describes the property and methods that provide information about the content represented an instance of the HttpContent class. (I have not differentiated between extension methods and those defined directly by HttpContent because there is no reason not to use the extension methods when writing a Web API application.)

Table 14-5. The Descriptive Members Defined by HttpContent

Name

Description

Headers

Returns an HttpContentHeaders object that contains the headers in the request

IsFormData()

Returns true if the Content-Type header is application/x-www-form-urlencoded

IsMimeMultipartContent()

Returns true if the Content-Type header indicates that MIME multipart encoding has been used for the request body

The methods described in Table 14-5 operate on the request headers. The request body isn’t read until it is required—which is why there is a limit of one value when using the binding features. Table 14-6 lists the most useful methods for reading the message body.

Table 14-6. The Methods Defined by HttpContent for Reading the Request Body

Name

Description

ReadAsStreamAsync()

Returns a Stream that can be used to read the raw contents of the request body

ReadAsStringAsync()

Returns the contents of the request body as a string

ReadAsFormDataAsync()

Returns a NameValueCollection containing name-value pairs parsed from x-www-form-urlencoded data

ReadAsMultipartAsync()

Returns a MultipartMemoryStreamProvider that parses the contents of a MIME multipart encoded body

The HttpContent class defines additional methods, including some that provide access to the parameter and model binding features. For this example, I know that jQuery will set the Content-Type header to application/x-www-form-encoded, which is normal for web applications sending form data, and that means I am interested in the IsFormData method to check the request content and the ReadAsFormDataAsync method to parse the data contained in the request body. Listing 14-24 shows the changes I made to the BindingsController to read data from the request body.

Listing 14-24. Reading Data from the Request Body in the BindingsController.cs File

using System.Web.Http;
using ExampleApp.Models;
using System.Linq;
using System.Net.Http;
using System.Collections.Generic;
using System.Net;
using System.Collections.Specialized;
using System.Threading.Tasks;

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

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

        [HttpGet]
        [HttpPost]
        public async Task<IHttpActionResult> SumNumbers() {
            if (Request.Content.IsFormData()) {
                NameValueCollection jqData = await Request.Content.ReadAsFormDataAsync();
                int firstValue, secondValue;
                if (TryGetValues(jqData, "first", "second", out firstValue,
                    out secondValue)) {
                        return Ok(firstValue + secondValue);
                } else if (TryGetValues(jqData, "value1", "value2", out firstValue,
                    out secondValue)) {
                        return Ok(firstValue - secondValue);
                }
            }
            return StatusCode(HttpStatusCode.BadRequest);
        }

        private bool TryGetValues(NameValueCollection data, string key1,
                string key2, out int val1, out int val2) {
            string val1string, val2string;
            val1 = val2 = 0;
            return (val1string = data[key1]) != null
                && int.TryParse(val1string, out val1)
                && (val2string = data[key2]) != null
                && int.TryParse(val2string, out val2);
        }
    }
}

Most of the changes relate to getting values from the collection that holds the parsed request data. The ReadAsFormDataAsync method returns an instance of the System.Collections.Specialized.NameValueCollection class, which presents a different API than the Dictionary I used in the previous example.

Image Tip  Notice that the methods that read the request body are asynchronous. I used the async keyword on the SumNumbers method and changed the result to Task<IHttpActionResult>. Within the method body I used the await keyword when calling the ReadAsFormDataAsync method.

I need to change the client-side code to send a POST request with the data in the request body to test the new implementation of the SumNumbers method. Listing 14-25 shows the changes that I made to the bindings.js file.

Listing 14-25. Sending a POST Request in the bindings.js File

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

There is no visible change to the way that the client works, but you can use the browser F12 tools to check that a POST request is being sent and processed by the web service. This example demonstrates that it is possible to get data directly from the request, but at the cost of having to check that the required values exist and can be parsed into the required types.

Summary

In this chapter, I introduced you to the Web API parameter and model binding features and explained how they work by default. I demonstrated how you can change the behavior by using the FromUri and FromBody attributes and how you can define a simple binding rule that has the same effect throughout a Web API application. I finished this chapter by showing you how to sidestep the parameter and model binding processes and work directly with the HttpRequestMessage object. There are occasions when this can be useful, but you should use the binding features whenever possible because it frees you from the tedious and error-prone tasks of finding and parsing data values. In Chapter 15, I dig into the details of parameter binding and explain what happens behind the scenes—and how you can take control of the process.

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

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