Sunday, September 23, 2018

What is MediaTypeFormatter

MediaTypeFormatter is an abstract class from which JsonMediaTypeFormatter and XmlMediaTypeFormatter classes inherit from. JsonMediaTypeFormatter handles JSON and XmlMediaTypeFormatter handles XML.

How to return only JSON from ASP.NET Web API Service irrespective of the Accept header value
Include the following line in Register() method of WebApiConfig.cs file in App_Start folder. This line of code completely removes XmlFormatter which forces ASP.NET Web API to always return JSON irrespective of the Accept header value in the client request. Use this technique when you want your service to support only JSON and not XML. 

With this change, irrespective of the Accept header value (application/xml or application/json), the Web API service is always going to return JSON.

config.Formatters.Remove(config.Formatters.XmlFormatter);

How to return only XML from ASP.NET Web API Service irrespective of the Accept header value
Include the following line in Register() method of WebApiConfig.cs file in App_Start folder. This line of code completely removes JsonFormatter which forces ASP.NET Web API to always return XML irrespective of the Accept header value in the client request. Use this technique when you want your service to support only XML and not JSON.

config.Formatters.Remove(config.Formatters.JsonFormatter);

With this change, irrespective of the Accept header value (application/xml or application/json), the Web API service is always going to return XML.

How to return JSON instead of XML from ASP.NET Web API Service when a request is made from the browser. 
So here is what we want the service to do
1. When a request is issued from the browser, the web API service should return JSON instead of XML. 
2. When a request is issued from a tool like fiddler the Accept header value should be respected. This means if the Accept header is set to application/xml the service should return XML and if it is set to application/json the service should return JSON.

There are 2 ways to achieve this

Approach 1 : Include the following line in Register() method of WebApiConfig.cs file in App_Start folder. This tells ASP.NET Web API to use JsonFormatter when a request is made for text/html which is the default for most browsers. The problem with this approach is that Content-Type header of the response is set to text/html which is misleading.

config.Formatters.JsonFormatter.SupportedMediaTypes
    .Add(new MediaTypeHeaderValue("text/html"));

Approach 2 : Include the following class in WebApiConfig.cs file in App_Start folder. 

public class CustomJsonFormatter : JsonMediaTypeFormatter
{
    public CustomJsonFormatter()
    {
        this.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));
    }

    public override void SetDefaultContentHeaders(Type type, HttpContentHeadersheaders, MediaTypeHeaderValue mediaType)
    {
        base.SetDefaultContentHeaders(type, headers, mediaType);
        headers.ContentType = new MediaTypeHeaderValue("application/json");
    }
}

Register the formatter: Place the following line in Register() method of WebApiConfig.cs file in App_Start folder
config.Formatters.Add(new CustomJsonFormatter());

With these 2 changes, when a request is issued from the browser you will get JSON formatted data and the Content-Type header of the response is also set to application/json. If you are using tools like fiddler and if you set Accept header to application/xml you will still get XML formatted data.

ASP.NET Web API is an extinsible framework. This means you can also plugin your own custom formatter. For example, if you want the response to be in CSV format, you can create custom CSVMediaTypeFormatter that inherits from the base abstract class MediaTypeFormatter . The following article describes how to do this.
http://www.tugberkugurlu.com/archive/creating-custom-csvmediatypeformatter-in-asp-net-web-api-for-comma-separated-values-csv-format 

ASP NET Web API Content Negotiation

One of the standards of the RESTful service is that, the client should have the ability to decide in which format they want the response - XML, JSON etc. A request that is sent to the server includes an Accept header. Using the Accept header the client can specify the format for the response. For example

Accept: application/xml returns XML
Accept: application/json returns JSON

Depending on the Accept header value in the request, the server sends the response. This is called Content Negotiation. 

So what does the Web API do when we request for data in a specific format
The Web API controller generates the data that we want to send to the client. For example, if you have asked for list of employees. The controller generates the list of employees, and hands the data to the Web API pipeline which then looks at the Accept header and depending on the format that the client has requested, Web API will choose the appropriate formatter. For example, if the client has requested for XML data, Web API uses XML formatter. If the client has requested for JSON data, Web API uses JSON formatter. These formatters are called Media type formatters.

ASP.NET Web API is greatly extensible. This means we can also plugin our own formatters, for custom formatting the data.

Multiple values can also be specified for the Accept header. In this case, the server picks the first formatter which is a JSON formatter and formats the data in JSON.
Accept: application/xml,application/json

You can also specify quality factor. In the example below, xml has higher quality factor than json, so the server uses XML formatter and formats the data in XML.
application/xml;q=0.8,application/json;q=0.5

If you don't specify the Accept header, by default the Web API returns JSON data.

When the response is being sent to the client in the requested format, notice that the Content-Type header of the response is set to the appropriate value. For example, if the client has requested application/xml, the server send the data in XML format and also sets the Content-Type=application/xml.

The formatters are used by the server for both request and response messages. When the client sends a request to the server, we set the Content-Type header to the appropriate value to let the server know the format of the data that we are sending. For example, if the client is sending JSON data, the Content-Type header is set to application/json. The server knows it is dealing with JSON data, so it uses JSON formatter to convert JSON data to .NET Type. Similarly when a response is being sent from the server to the client, depending on the Accept header value, the appropriate formatter is used to convert .NET type to JSON, XML etc.

It's also very easy to change the serialization settings of these formatters. For example, if you want the JSON data to be properly indented and use camel case instead of pascal case for property names, all you have to do is modify the serialization settings of JSON formatters as shown below. With our example this code goes in WebApiConfig.cs file in App_Start folder.

config.Formatters.JsonFormatter.SerializerSettings.Formatting =
                            Newtonsoft.Json.Formatting.Indented;
config.Formatters.JsonFormatter.SerializerSettings.ContractResolver =
    new CamelCasePropertyNamesContractResolver();

Calling ASP.NET Web API service in a cross domain using jQuery ajax

What is same origin policy
Browsers allow a web page to make AJAX requests only with in the same domain. Browser security prevents a web page from making AJAX requests to another domain. This is called same origin policy. 

The following 2 URLs have the same origin
http://localhost:1234/api/employees
http://localhost:1234/Employees.html

The following 2 URLs have different origins, because they have different port numbers (1234 v/s 5678)
http://localhost:1234/api/employees
http://localhost:5678/Employees.html

The following 2 URLs have different origins, because they have different domains (.com v/s .net)
http://pragimtech.com/api/employees
http://pragimtech.net/Employees.html

The following 2 URLs have different origins, because they have different schemes (http v/s https)
https://pragimtech.com/api/employees
http://pragimtech.net/Employees.html

To prove browsers does not allow cross domain ajax requests, let's add a new web forms project to EmployeeService solution. Name it ClientApplication. Add an HTML page. Name it HtmlPage1.html. Copy and paste the following HTML and jQuery code.

<!DOCTYPE html>
<html>
<head>
    <title></title>
    <meta charset="utf-8" />
    <script src="Scripts/jquery-1.10.2.js"></script>
    <script type="text/javascript">
        $(document).ready(function () {
            var ulEmployees = $('#ulEmployees');

            $('#btn').click(function () {
                $.ajax({
                    type: 'GET',
                    // Make sure to change the port number to
                    // where you have the employee service
                    // running on your local machine
                    url: 'http://localhost:23258/api/Employees',
                    dataType: 'json',
                    success: function (data) {
                        ulEmployees.empty();
                        $.each(data, function (index, val) {
                            var fullName = val.FirstName + ' ' + val.LastName;
                            ulEmployees.append('<li>' + fullName + '</li>')
                        });
                    }
                });
            });

            $('#btnClear').click(function () {
                ulEmployees.empty();
            });
        });
    </script>
</head>
<body>
    <input id="btn" type="button" value="Get All Employees" />
    <input id="btnClear" type="button" value="Clear" />
    <ul id="ulEmployees"></ul>
</body>
</html>

When you click "Get All Employees" button on "HtmlPage1.html" page, you get the following error. To see the error launch browser tools and click on the console tab.
XMLHttpRequest cannot load http://localhost:23258/api/Employees. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:6293' is therefore not allowed access.

On the other hand when you click "Get All Employees" button on "Employees.html"page that is present in the same project as the ASP.NET Web API Service, that employee data is displayed without any problem.

So this proves, browsers does not allow cross domain ajax requests. There are 2 ways to get around this problem
  • Using JSONP (JSON with Padding) 
  • Enabling CORS (Cross Origin Resource Sharing)
In this video let's use JSONP to overcome the browser cross-domain restriction. In our next video we will discuss enabling CORS. 

So what is JSONP and what does it do?
JSONP stands for JSON with Padding. All JSONP does is wraps the data in a function. So for example, if you have the following JSON object
{
    "FirstName" : "Mark",
    "LastName"  : "Hastings",
    "Gender"    : "Male",
}

JSONP will wrap the data in a function as shown below
CallbackFunction({
    "FirstName" : "Mark",
    "LastName"  : "Hastings",
    "Gender"    : "Male",
})

Browsers allow to consume JavaScript that is present in a different domain but not data. Since the data is wrapped in a JavaScript function, this can be consumed by a web page that is present in a different domain.

Steps to make ASP.NET Web API Service to return JSONP formatted data and consume it from a cross domain ajax request
Step 1 : To support JSONP format, execute the following command using NuGet Package Manager Console which installs WebApiContrib.Formatting.Jsonp package.
Install-Package WebApiContrib.Formatting.Jsonp

Step 2 : Include the following 2 lines of code in Register() method of WebApiConfig class in WebApiConfig.cs file in App_Start folder

var jsonpFormatter = newJsonpMediaTypeFormatter(config.Formatters.JsonFormatter);
config.Formatters.Insert(0, jsonpFormatter);

Step 3 : In the ClientApplication, set the dataType option of the jQuery ajax function to jsonp 
dataType: 'jsonp'

Testing the ASP.NET Web API Service using fiddler
Notice in fiddler we have just specified the URL of the Web API service without Accept header and callback function in the URI 
asp.net web api jsonp example 

The above request results in the following error
A callback parameter was not provided in the request URI

If you want JSON data back, set Accept header to application/json and there is no need to specify the callback function in the URI. The request completes successfully. 
asp.net web api crossdomain 

If you want JSONP formatted data back, set Accept header to application/javascriptand specify a name for the callback function in the URI. We have set it to ABC. 
asp.net web api ajax cross domain 

Find the value from array when age is more than 30

 const data = [   { id: 1, name: 'Alice', age: 25 },   { id: 2, name: 'Bob', age: 30 },   { id: 3, name: 'Charlie', ...