Overview

 

In today’s web development, it is pretty common practice to load data using WebApi / Rest based service from Client side script (Jquery/AngularJS). There is no issue when consumer web application and rest/api services are hosted in the same domain, but when web-based applications are trying to load data from a domain which is not under your control, the chances are that you’ve seen the following message in your browser’s console:

XMLHttpRequest cannot load http://external-domain/service. No ‘Access-Control-Allow-Origin’ header is present on the requested resource. Origin ‘http://my-domain’ is therefore not allowed access.

 

In this article, we’ll look at what causes this error and how we can get around it by using jQuery and JSONP to make a cross-domain Ajax call.

What is same origin policy?

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

 

Regular web pages can use the XMLHttpRequest object to send and receive data from remote servers, however, they’re often restricted in what they perform by the same-origin-policy. This is an important concept in the browser security model and controls that a web browser may only allow scripts on page A to access data on page B if these two pages have the same origin. The origin of a page is defined as a combination of URI Scheme, host name, and port number. This policy prevents a malicious script on one page from obtaining access to sensitive data on another web page.

 

The following 2 URLs have the same origin

http://localhost: 59736/api/customers
http://localhost: 59736/defualt.html

The following 2 URLs have different origins because they have different port numbers (59736v/s 59705)
http://localhost: 59736/api/customers
http://localhost: 59705/default.html

The following 2 URLs have different origins because they have different domains (.com v/s .net)
http://Sampletech.com/api/Customers
http://Sampletech.net/default.html

The following 2 URLs have different origins because they have different schemes (http v/s https)
https://Sampletech.com/api/customers
http://Sampletech.com/default.html

To prove browsers does not allow cross domain ajax requests, Lets’s add simple web API which returns the All customer details.
Here is Code Snippet for Web Api:
Customer Model Class:
public class Customer
{
public int ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Phone { get; set; }
}

Web Api Controller Class:

namespace SampleWebApi.Controllers
{
[RoutePrefix(“api/Customer”)]
public class CustomerController : ApiController
{
// GET api/Customer
public IHttpActionResult Get()
{
List CustList = new List();
CustList.Add(new Customer() { ID = 1, FirstName = “GILBERT”, LastName = “SMITH”, Phone = “408629-0589” });
CustList.Add(new Customer() { ID = 2, FirstName = “R”, LastName = “BATTERSBY”, Phone = “919/489-6792” });
CustList.Add(new Customer() { ID = 3, FirstName = “LEONARD”, LastName = “DOS REMEDIOS”, Phone = “301/760-2541” });
CustList.Add(new Customer() { ID = 4, FirstName = “ITO”, LastName = “TAYLOR-HUNYADI”, Phone = “(02)933-3212” });
CustList.Add(new Customer() { ID = 5, FirstName = “SHIEKELESLAM”, LastName = “SHALA”, Phone = “03/734-5111” });
CustList.Add(new Customer() { ID = 6, FirstName = “DUNNETT”, LastName = “CHRISTINE”, Phone = “(03)022-2332” });
return Ok(CustList);
}
}
}
If we access above API from browser, the customer Api would return the all existing Customers.
API URL in my system is “http://localhost:59736/api/Customer”

Let’s add a new client asp.net web project and adddefault.html Copy and paste the following HTML and jQuery code.

 

$(document).ready(function () {
var ulCustomers= $(‘#ulCustomers’);

$(‘#btn’).click(function () {
$.ajax({
type: ‘GET’,
// Make sure to change the port number to
// where you have the Customer service
// running on your local machine
url: ‘http://localhost:59736/api/Customer’,
dataType: ‘json’,
success: function (data) {
ulCustomers.empty();
$.each(data, function (index, val) {
var fullName = val.FirstName + ‘ ‘ + val.LastName;
ulCustomers.append(‘

  • ‘ + fullName + ‘

 

‘)
});
}
});
});

$(‘#btnClear’).click(function () {
ulCustomers.empty();
});
});

     

    When you click “Get All Customers” button on “default.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:59736/api/Customer. No ‘Access-Control-Allow-Origin’ header is present on the requested resource. Origin ‘http://localhost:59705’ is therefore not allowed access

     

    On the other hand If API and client html page on same origin (same port) and click “Get All Customers” could have Customers data displayed without any problem. But in above scenario port number for client application and web api url is different, so as per the browser security policy is not allowing ajax to access different domain content.

     

    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)

     

    CORS can be used as a modern alternative to the JSONP pattern. While JSONP supports only the GET request method, CORS also supports other types of HTTP requests. Using CORS enables a web programmer to use regular XMLHttpRequest, which supports better error handling than JSONP. On the other hand, JSONP works on legacy browsers which predate CORS support. CORS is supported by most modern web browsers. Also, while JSONP can cause cross-site scripting (XSS) issues where the external site is compromised, CORS allows websites to manually parse responses to ensure security.

     

    In this blog, let’s use JSONP to overcome the browser cross-domain restriction.

     

    So what is JSONP and what does it do?

    JSONP (which stands for JSON with Padding) builds on this technique and provides us with a way to access the returned data. It does this by having the server return JSON data wrapped in a function call (the “padding”) which can then be interpreted by the browser. This function must be defined in the page evaluating the JSONP response.

     

    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” : “SHIEKELESLAM”,
    “LastName” : “SHALA”,
    “Phone” : “502(734)-5111”,
    }

    JSONP will wrap the data in a function as shown below
    CallbackFunction({
    “FirstName” : “SHIEKELESLAM”,
    “LastName” : “SHALA”,
    “Phone” : “502(734)-5111”,
    })

     

    The interesting fact that Browsers allow consuming 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 3 lines of code in Application_Start () method of Global.asax.cs class
    var config = GlobalConfiguration.Configuration;
    var jsonpFormatter = new JsonpMediaTypeFormatter(config.Formatters.JsonFormatter);
    config.Formatters.Insert(0, jsonpFormatter);

     

    Web API Global.asax.cs code snippet:
    using WebApiContrib.Formatting.Jsonp;

    namespace SampleWebApi
    {
    public class WebApiApplication : System.Web.HttpApplication
    {
    protected void Application_Start()
    {
    AreaRegistration.RegisterAllAreas();
    GlobalConfiguration.Configure(WebApiConfig.Register);
    FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
    RouteConfig.RegisterRoutes(RouteTable.Routes);
    BundleConfig.RegisterBundles(BundleTable.Bundles);

    var config = GlobalConfiguration.Configuration;
    var jsonpFormatter = new JsonpMediaTypeFormatter(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’

     

    After proceeding with above 3 steps, build the solution and access from browser must be populated Customer data on the Screen.

     

    Conclusion

    JSONP enables users to avoid the same-origin policy and to some extent make cross-domain Ajax calls. It is simply not a silver bullet and it undeniably has its own issues as well; however, in some cases, it can prove helpful when you need to fetch data from a different origin.
    JSONP also makes it possible to extract a variety of content from various services. Many leading sites provide JSONP services (for example Flickr), allows users to access its content via a predefined API.

    Naga
    Naga
    Technical Architect