An adventure in JSONP and CORS

It’s important to grasp the concepts of Same Origin Policy restriction and JSONP. Imagine an application on application.org trying to use AJAX to get data from dataprovider.org. This cross-domain request triggers the Same Origin Policy, causing the AJAX call to fail. To overcome this security feature (which acts as a constraint), JSONP was devised.

Instead of a direct AJAX call, a script tag is dynamically added to the document. This tag points to the ‘foreign’ domain (dataprovider.org in this case). The server then responds with Javascript code containing the data wrapped in a function call. This function, pre-defined on the client-side (application.org), handles the data.

However, this method has limitations:

  • GET requests only: Complex JSON objects in POST requests are problematic due to URL encoding limitations.
  • Server-side adaptation: The server must wrap data in the client-specified Javascript function call.

During my project work, which involves JSONP, I encountered an interesting issue. Despite running servers on localhost with different ports (which should technically bypass the Same Origin Policy), my AJAX call, expected to fail, seemed to reach the server:

[Image: https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh5cwv1oRj0f_n9EO2wIv6kgVNTFE2F6jtReM0Xt4eZRPFMLGIgJ89ghTP30qArnGQWR4gm6JwQdmt-8rEgtu40MJolab0bmfdWu5xvw9ypGsclV-2OJj9Ct17bNFMAFvitIS3upKikRVQU/s320/CropperCapture%5B118%5D.jpg]

Firebug displayed a ‘200 OK’ response, yet jQuery’s AJAX error callback was triggered with a status of 0. This was puzzling because the browser should have raised a cross-domain error before contacting the server, as confirmed by http://en.wikipedia.org/wiki/XMLHttpRequest">Wikipedia.

[Image: https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgz-VCGZT8B4pBgsh0AZpqgiwu-Xep_LC_kCzGAB5xU9CjuYodEbO_rSDO80CDDOTHsrnUh3c1_ZjHN8rZxUetArLpQKSBLQNHsjqQrfjJ1HFnsZ7sHEvZshmuexH4ADDRDLOzIqULKEVKd/s320/CropperCapture%5B119%5D.jpg]

The culprits seem to be http://en.wikipedia.org/wiki/Cross-origin_resource_sharing">CORS</a and my development server (IIS Express), as explained in the https://developer.mozilla.org/en-US/docs/HTTP/Access_control_CORS?redirectlocale=en-US&redirectslug=HTTP_access_control">This document on MDN (which provides a good overview of CORS).

My understanding is that modern browsers like Firefox don’t immediately block cross-domain AJAX. They send the request with an ‘Origin’ header and await the server’s response. Ideally, a server should validate this header and respond accordingly. IIS Express, likely not CORS-aware, allows the request, mimicking a same-domain call. Firefox, missing the necessary ‘Access-Control-Allow-Origin’ header in the response, throws an error.

Another point of confusion is jQuery’s JSONP implementation. Using $.ajax with dataType: "jsonp" (instead of “json”) should theoretically handle cross-domain requests (limited to GET requests). This makes things seem both simple and unclear. No explicit JSONP callback function declaration is needed, and the success/error callbacks appear confusing as it’s not a typical AJAX call.

jQuery takes care of everything: creating the script element, generating a client-side callback to invoke the success/error callbacks, and appending it to the query string. So, the documentation stating:

Adds an extra “?callback=?” to the end of your URL to specify the callback

means the script’s src will resemble:

http://localhost:55579/rpc/Geographic/GetCitiesJsonp?callback=jQuery1710571054381039827_1361460447827&_=1361460450663

with the server responding with something like:

jQuery1710571054381039827_1361460447827(["Berlin","Leipzig","Dresden"]);

[Image: https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj-bfT3WlgGO4dLPRfnhe0v8kQKdfq5ZgxMbiU7POD5K06RXCc8x1sxGH9K24J771IREbTth4bf-IuGU0iy7axEKYOSx84ykRUKoDjWyKhsfnfO2Je4BrejS2Y2MhT5OUH_t5fdZOMEzUDv/s320/CropperCapture%5B120%5D.jpg]

While convenient, using AJAX semantics for non-AJAX operations feels misleading. A dedicated $.jsonp method would have been more appropriate.

https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh5cwv1oRj0f_n9EO2wIv6kgVNTFE2F6jtReM0Xt4eZRPFMLGIgJ89ghTP30qArnGQWR4gm6JwQdmt-8rEgtu40MJolab0bmfdWu5xvw9ypGsclV-2OJj9Ct17bNFMAFvitIS3upKikRVQU/s1600/CropperCapture%5B118%5D.jpg

https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgz-VCGZT8B4pBgsh0AZpqgiwu-Xep_LC_kCzGAB5xU9CjuYodEbO_rSDO80CDDOTHsrnUh3c1_ZjHN8rZxUetArLpQKSBLQNHsjqQrfjJ1HFnsZ7sHEvZshmuexH4ADDRDLOzIqULKEVKd/s1600/CropperCapture%5B119%5D.jpg

https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj-bfT3WlgGO4dLPRfnhe0v8kQKdfq5ZgxMbiU7POD5K06RXCc8x1sxGH9K24J771IREbTth4bf-IuGU0iy7axEKYOSx84ykRUKoDjWyKhsfnfO2Je4BrejS2Y2MhT5OUH_t5fdZOMEzUDv/s1600/CropperCapture%5B120%5D.jpg

Licensed under CC BY-NC-SA 4.0
Last updated on Mar 22, 2024 01:24 +0100