CHAPTER 7
As we already know from Chapter 1, the client can ask for the resource in a specific format using the Accept header. With this, the server will try to serve the request to match the specific format and if the format is not supported, it returns a 406 Not Acceptable.
The Web API uses a content negotiation mechanism to support this functionality. We saw in the previous chapter that all the responses are served as JSON, but we can ask for XML format too.
Consider the Post example and build a request like this:

A GET that specifies the accept type
We simply add the Accept header and set it to text/xml. This means that the client asks for the Post with an Id equal to 1 in XML format. In fact, the server responds with an XML response with elements that match the attributes of the Post object.
This works because the ASP.NET Web API has the XmlFormatter included in the list of available formatters.
Formatters are objects that inherit from MediaTypeFormatter, an abstract class with the following interface:
public abstract class MediaTypeFormatter { Collection<MediaTypeHeaderValue> SupportedMediaTypes { get; } Collection<Encoding> SupportedEncodings { get; } Collection<MediaTypeMapping> MediaTypeMappings { get; } IRequiredMemberSelector RequiredMemberSelector { get; set; } Task<object> ReadFromStreamAsync(Type type, Stream readStream, HttpContent content, IFormatterLogger formatterLogger); Task WriteToStreamAsync(Type type, object value, Stream writeStream, HttpContent content, TransportContext transportContext); Encoding SelectCharacterEncoding(HttpContentHeaders contentHeaders); void SetDefaultContentHeaders(Type type, HttpContentHeaders headers, MediaTypeHeaderValue mediaType); MediaTypeFormatter GetPerRequestFormatterInstance(Type type, HttpRequestMessage request, MediaTypeHeaderValue mediaType); bool CanReadType(Type type); bool CanWriteType(Type type); } |
At first look, we could see that a formatter can read and write data. This makes sense because a formatter can parse the request in a specific format to build the parameters for the actions, and on the other side can convert the result of an action to another specific format.
Given that the most important members are:
If we want, we can add our own formatters to match the API specification.
Let's look at an example to see what we can do with a media type formatter. Consider the following controller:
public class AuthorsController : ApiController { // ... public IQueryable<Author> Get() { return _repository.GetAll(); } public Author Get(int id) { return _repository.Get(id); } } |
It is a simple controller with two methods to get a list or a single author. The author class is something like this:
|
{ public int Id { get; set; } public string Name { get; set; } public string PhotoUrl { get; set; } } |
So if we request a single author, we see:

Getting a single author
What we want now is the photo of the author, but we would like to use the same URI we used to obtain the author info (/api/authors/1).
Therefore, we create a new GET request with the Accept header that specifies which format we want (for example, image/png).

A get request that specifies the accept type
As a response, we want a PNG image that represents Tolkien (the author with id 1).
To obtain this we need the help of a MediaTypeFormatter that takes the Author object from the controller and replaces it with his photo.
This is the complete implementation:
This class inherits from MediaTypeFormatter, a base class for all the formatters. The runtime calls the WriteToStreamAsync, as noted previously. In this method, since it is asynchronous, we create a new Task and start it with the methods that actually read a file from the file system and write it to the output stream.
To add the newly created ImageFormatter media type formatter to the Web API pipeline, we need to register it by using the Formatters property on the HttpConfiguration object when the application starts:
public static class WebApiConfig { public static void Register(HttpConfiguration config) { config.Formatters.Add(new ImageFormatter()); } } |
The result when invoking the service is as follows:

The response in PNG format
The nice thing is that with the same URI (remember that the URI represents the ID of a resource), we have two different representations, and we can decide which one we want using different headers.
Content negotiation is an important part of a real REST API. It makes it possible to expose a resource in various formats and let the client decide which is best. We saw how to implement a custom formatter to transform a resource in a PNG format so that the image representation of the resource could be provided to the client.