January 2018 : Instructor-led Online Course in ASP.NET Core 2.0. Conducted by Bipin Joshi. Read more...
Registration for January 2018 batch of ASP.NET Core 2.0 instructor-led online course has already started. Conducted by Bipin Joshi. Register today ! Click here for more details.

Untitled 1

Content Negotiation in Web API

Whenever you access ASP.NET Web API from your client side script (JavaScript / jQuery) by default the data is returned in JSON format. However, in certain cases you may want to retrieve the data in XML format instead of JSON. On the server side, Web API determinses what data format to use for sending data to the client by doing what is known as Content Negotiation. Simply put, content negotiation is a process by which Web API inspects the incoming request and HTTP headers accompanying the request to figure out what response format(s) the client can understand. Based on this checking Web API sends the output. Two HTTP headers that play role in content negotiation are:

  • Accept
  • Content-Type

To understand how content negotiation works let's create a simple Customer Web API that returns customer data from the Northwind database.

Begin by creating a new ASP.NET MVC 4 project in Visual Studio 2012. You will need to add ADO.NET Entity Framework data model for the Customers table. The following figure shows the Customer class upon adding the data model.

Next, add a new Web API controller to the Controllers folder and name it as CustomerController. The CustomerController needs to have just one method - Get() - as shown below:

public class CustomerController : ApiController
{
  public IEnumerable<Customer> Get()
  {
    NorthwindEntities db=new NorthwindEntities();
    var data = (from item in db.Customers
    select item).Take(10);
    return data;
  }
}

As you can see the Get() method simply returns first 10 Customer records from the Customers table. To call this Web API from a client browser, add a new controller and name it as HomeController. Then add a view for the Index() action method of the HomeController.

public class HomeController : Controller
{
  public ActionResult Index()
  {
    return View();
  }
}

Inside the Index view, add the following jQuery code that calls the Customer Web API:

$(document).ready(function () {
  var options = {};
  options.url = "/api/customer";
  options.type = "GET";
  options.success = OnSuccessJSON;
  options.error = OnError;
  $.ajax(options);
});

function OnSuccessJSON(results) {
  var html = "<table border=1 cellpadding=3>";
  for (var i = 0; i < results.length; i++) {
    html += "<tr>";
    html += "<td>" + results[i].CustomerID + "</td>";
    html += "<td>" + results[i].CompanyName + "</td>";
    html += "</tr>";
  }
  html += "</table>";
  $("#container").html(html);
}

function OnError(err) {
  alert(err.status + " - " + err.statusText);
}

The above code uses $.ajax() of jQuery to call the Customer Web API. The options object stores all the settings that are needed to call our Web API. Notice that currently we have not specified Accept and Content-Type headers. The OnSuccessJSON() function handles the successful request and displays the returned data in an HTML table. As mentioned earlier, by default Web API invoked via client side script gets data from the server in JSON format. That is why the OnSuccessJSON() function treats results object like any other JSON array. After generating HTML markup for table the markup is set to the container <div> element.

The OnError() function is called in case there is any error while calling the Web API.

If you run the above view you should see the HTML table populated with customer data as shown below:

 Now let's modify our jQuery code so that the Web API sends us data in XML format instead of JSON. Modify the options object to include Accept header as shown below:

 ...
options.type = "GET";
options.headers = { "accept": "application/xml;charset=utf-8" };
...

As you can see you use the headers setting. Set its key to accept and value to application/xml;charset=utf-8. This value tells Web API that data should be sent in XML format. You will also need to write another success handler function because now the data is in XML format instead of JSON. Let's call that function OnSuccessXML().

function OnSuccessXML(results) {
  var html = "<table border=1 cellpadding=3>";
  $(results).find("Customer").each(function () {
    var item = $(this);
    html += "<tr>";
    html += "<td>" + item.find("CustomerID").text() + "</td>";
    html += "<td>" + item.find("CompanyName").text() + "</td>";
    html += "</tr>";
  });
  html += "</table>";
  $("#container").html(html);
}

The OnSuccessXML() function uses find() of jQuery to locate <Customer> elements from the response XML and for each <Customer> the code specified inside each() is executed. The each() function further finds <CustomerID> and <CompanyName> elements from the response XML and an HTML table is generated. Finally, the generated HTML markup is assigned to the container <div> element.

Make sure to change the success function and then run the new code:

 ...
options.success = OnSuccessXML;
... 

In the above code you used Accept header but what's the role of Content-Type header? The Content-Type header is used in situations where response format as suggested by Accept header can't be sent. To understand this modify the jQuery code as shown below:

...
options.type = "GET";
options.contentType = "application/json;charset=utf-8";
options.headers = { "accept": "application/xml123;charset=utf-8" };
...

Notice carefully that accept header contains invalid format (xml123) and contentType setting says the format to be JSON. In this case since xml123 is an unsupported format, Web API will use format as suggested by Content-Type header (JSON in this case). You can confirm this by wiring OnSuccessJSON() function again.

The following table summarizes various combinations of Accept and Content-Type headers:

Accept Content-Type Response Format
Yes No As per Accept header value
Yes Yes As per Accept header value
Yes but invalid Yes As per Content-Type header value
No Yes As per Content-Type header value
No or invalid No or invalid Depending on the default sequence of media formatters specified in the Configuration.Formatters list.

The last option from the above table can be changed via Web API code but that's beyond the scope of this article.

That's it! Keep coding !!

 


Bipin Joshi is a software consultant, an author and a yoga mentor having 22+ years of experience in software development. He also conducts online courses in ASP.NET MVC / Core and Design Patterns. He is a published author and has authored or co-authored books for Apress and Wrox press. Having embraced the Yoga way of life he also teaches Meditation and Mindfulness to interested individuals. To know more about him click here.

Get connected : Twitter  Facebook  Google+  LinkedIn

Posted On : 23 April 2013


Tags : ASP.NET MVC C# jQuery