Content Negotiation And Response Types

Type an address into the browser and the HTTP request from the browser contains a header like this;


HTTP GET /foo/bar
Accept: text/html,application/xhtml+xml,...

and this `Accept` header is a signal to the application of the kind of content that it’s expecting back. If the browser responds with a web page, it might have a response header like so;

Content-Type: text/html
<!DOCTYPE HTML>
<html lang="en">
<...

So there’s a conversation going on here; the client says ‘I’d like the HTML at /foo/bar’ and the server says ‘Here’s a document!’ That’s the basic pattern repeated over and over during normal web browsing. But this system is fairly flexible. What if /foo/bar represents a resource and you don’t want HTML, but want JSON instead? You can request this;


HTTP GET /foo/bar
Accept: application/json

And if the server can respond with JSON, it will. This is the sort of thing jQuery does when you write


$.getJSON(...);

This is really interesting, because it means that the same URL, with the same verb, can return two totally different representations. This process, this interaction between the `Accept` and `Content-Type` headers, is known as *content negotiation*.

So this little mechanism has some interesting applications. Imagine you are doing a project management app, and you decide to use a URL like this;


/api/Projects/1

This URL represents the resource of ‘Project #1’. But it doesn’t imply anything about the form of the response. If we request


GET /api/Projects/1 HTTP/1.0
Accept: application/json

Then we might expect the response body to contain


{
"ProjectId": 1,
"Name", "Adveriting Campaign"
}

But if we make this request;


GET /api/Projects/1 HTTP/1.0
Accept: application/xml

Then we might expect to recieve this body;


<Project>
<ProjectId>1</ProjectId>
<Name>Advertising Campaign</Name>
</Project>

Now, this is what you see in the OData specification — the same resource, at the same URI, with two different representations. And you’re not just limited to two. Why not return an HTML description if the request asks for `text/html`, or YAML if that’s what the client asks for?

WebAPI lets you add new representations to your system. In your Global.asax, you’ll have an `HttpConfiguration` instance floating around — it’s the object you add routes to. But it also has a member called `Formatters` which is a list of message formatters. The default list includes formatters for JSON, XML, and Form Url Encoding. The first two are for Web/HTTP/REST-style apps; the third is to interpret HTML Form posts. These objects do bi-directional translation; so that if your controller method looks like this;


public Project Get(int id)
{
... do work here...
}

It’s the formatter which determines how the `Project` instance returned by your method is converted into the JSON or XML returned in your HTTP response. Add a new formatter, and your system is now capable of returning a new representation without you having to change your controller method at all. Sweet!

And this also works the other way. The same formatter will take an HTTP request and turn it back into object instances, so that something like this;


public void Put(Project project)
{
... update your project here ...
}

Has the same process — the `Project` instance passed as a parameter has been constructed by the object in config.Formatters. This process is known as Model binding. if you want to look into it further. An example — binding using the Google protobuf format — exists on gitHub in the WebAPIContrib project — https://WebAPIContrib/WebAPIContrib.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s