Let's see how the ASP.NET Web API choose the response format to send as response.

In sample applications, we returned result from Web API in similar to below format:

public Team Get(int id)
{
return _teams.First(t => t.Id == id);
}

Ever wondered, how the Team class object get converted in JSON(or XML) format while receiving that in our client application?

Let's dig this now:
According to ASP.NET Web API, translating .Net CLR(Common Language Runtime) type into a format, understandable over http(JSON or XML), is called serialization. To accomplish this task, .Net has MediaTypeFormatter.
So, when our above Get action method returns CLR object of type Team, MediaTypeFormatter object, in ASP.NET Web API pipeline, performs serialization and serializes the CLR object into JSON or XML, which is then written into the response message body.

To support JSON and XML formats, there are 2 different formatters, JsonMediaTypeFormatter and XmlMediaTypeFormatter, both inherits from base class MediaTypeFormatter.

Now which formatter will be chosen for serialization, that depends upon the client, who is sending requesting to Web API.
For JSON: Accept: application/json
For XML: Accept: application/xml

We can also pass preference by specify quality value. Quality value ranges between 0 to 1.
So, if we send the request like below:
Accept: application/json; q=0.7, application/xml;q=0.8,
Then we will receive xml response, because that has high quality.

This process of selecting response format is called content negotiation or conneg.

Let’s dig this content negotiation process now:

System.Net.Http.Formatting.DefaultContentNegotiator class contains default content negotiator algorithm in Negotiate method. This method accepts three inputs:

- Type of the object to serialize

- Collection of media formatters

- Request object (HttpRequestMessage)

This Negotiate methods checks below items in order before deciding on the media formatter to use:

#1. Media type mapping: MediaTypeFormatter keeps a collection of MediaTypeMapping values. A MediaTypeMapping allows us to map the request or response messages to a specific media-type. There are four media type mappings:

- QueryStringMapping: Maps query string parameter

- UriPathExtensionMapping: Maps URI path extension

- RequestHeaderMapping: Maps request header

- MediaRangeMapping: media range

#2. Media type as specified in the Accept request header.

#3. Media type as specified in the Content-Type request header.

#4. If there is no match till now, the algorithm checks the MediaTypeFormatter defined in the config and checks if a formatter can handle and serialize that type by checking CanWriteType method. The first formatter that can serialize the type is chosen.

Enough theory! Let’s see through examples:

I will be using the project created in article ASP.NET WEB API Part 2 - Sample Application and we will also need tool like Fiddler to compose and fire request. If needed, can visit one of my previous article on Fiddler.

Let’s run the application and open Fiddler now.

#1. Go to the Composer tab and issue a GET request for
http://sample.dev/api/teams/100, by mentioning Accept: application/json in Request Headers box.

Now, notice the response; It will be JSON, similar to below:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 49
{"Id":100,"Title":"Team 100","Owner":"Owner 100"}

#2. Now lets change the Accept value to xml type. Accept: application/xml

And now call same URI.

Now, notice the response; It should be XML, similar to below:

HTTP/1.1 200 OK
Content-Type: application/xml; charset=utf-8
Content-Length: 192
<Team xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/SampleWebApi.Models">
<Id>100</Id>
<Owner>Owner 100</Owner>
<Title>Team 100</Title>
</Team>

#3. Now lets issue a request by using quality factor. For Accept mention below value:
application/xml;q=0.3, application/json;q=0.4

If we notice the response, we will see it will be JSON, as quality factor is more for that. We can also switch the quality factor and will notice the XML response.

#4. This time, let's not include Accept header. Just use: Content-Type: application/xml

We will notice the response as XML. And if we put Content-Type: applicatio/json, we will see JSON response.

As we did not mention any Accept header. So, content negotiator preferred Content-Type to choose media type.

#5. Now if we do a request without mentioning Accept or Content-Type header. We will notice the response as JSON. But, why JSON, why not XML!!!

As we did not pass any thing in request header; So, content negotiator just checked the list of available media-type formatters in the order, its listed there. And By default JSONMediaTypeFormatter used to be first.

#6. Let's remove JSONMediaTypeFormatter from the list and see the response.
Add below line in WebApiConfig class:

config.Formatters.RemoveAt(0); // Which is JSONMediaTypeFormatter

And then again run the application and fire a request without Accept or Content-Type.

Now, as there is no JSONMediaTypeFormatter in the list. So, it move to XMLMediaTypeFormatter and response will be of xml type.

As we did not pass any thing in request header; So, content negotiator just checked the list of available media-type formatters in the order, its listed there. And By default XMLMediaTypeFormatter used to be first, as we removed JSONMediaTypeFormatter(which was first).

#7. Let's remove XMLMediaTypeFormatter from the list too and see the response. :)
Again add below line in WebApiConfig class, after the line added in #6:

config.Formatters.RemoveAt(0); // Which is XMLMediaTypeFormatter

Run the application and fire a request with or without Accept or Content-Type.

We will notice 406 error: Not Acceptable

Why.... :)

Because, now our WebAPI does not have any formatter to serialize the Team object JSON/ XML.

Let's remove both the lines, which we added to remove formatters and move forward.

Above we also discussed about passing the format in query string.

Let's add below line of code in WebApiConfig class:

config.Formatters.JsonFormatter.MediaTypeMappings.Add(
new QueryStringMapping("format", "json",
new MediaTypeHeaderValue("application/json")));

config.Formatters.XmlFormatter.MediaTypeMappings.Add(
new QueryStringMapping("format", "xml",
new MediaTypeHeaderValue("application/xml")));

In above lines of code, we added media-type header, as per the value of format in query string .

#8. Now if we initiate a request through fiddler with below format of URI:
http://sample.dev/api/teams/100?format=json
In response, we will notice JSON format.

And for URI: http://sample.dev/api/teams/100?format=xml
We will see JSON response.

Now keep the URI as http://sample.dev/api/teams/100?format=xml
But add Accept header as application/json
Notice, in query string we are mentioning xml but in header we are sending json type.
So, what should be the response?
Recall the order of precedence followed by content negotiator(we discussed above), where QueryStringMapping takes precedence.

Summary

We saw how .Net CLR class object(s) get converted in desired HTTP format, while sending response to client.

What's the role of MediaTypeFormatter in this process.

How to decide which formatter needed to come in picture.


Stay tuned, if looks interesting.

Reference:
Book: Practical ASP.NET Web API [Apress]

Previous > ASP.NET WEB API Part 4 - Tracing

Next > ASP.NET WEB API Part 6 - Controlling members serialization

Discussion 1 Comment
42 / 7 =
** To prevent abusing comments from publishing, posted comments will be reviewed and then published!
 Mritunjay Kumar
Works at Mindfire Solutions

I mostly work with C#, ASP.NET, MVC, WCF, Web API, Entity FrameWork, MS Sql.