TL;DR
JSONP is an old trick invented to bypass the security restriction that forbids us to get JSON data that is in a different website (a different origin1) than the one we are navigating in.
The trick works by using a <script>
tag that asks for the JSON from that place, e.g.: { "user":"Smith" }
, but wrapped in a function, the actual JSONP ("JSON with Padding"):
peopleDataJSONP({"user":"Smith"})
Receiving it in this form enables us to use the data within our peopleDataJSONP
function. JSONP is a bad practice and not needed anymore, don't use it (read below).
The problem
Say we want to use on ourweb.com
some JSON data (or any raw data really) hosted at anotherweb.com
. If we were to use GET request (think XMLHttpRequest
, or fetch
call, $.ajax
, etc.), our browser would tell us it's not allowed with this ugly error:
How to get the data we want? Well, <script>
tags are not subjected to this whole server (origin1) restriction! That's why we can load a library like jQuery or Google Maps from any server, such as a CDN, without any errors.
Here's the important point: if you think about it, those libraries are actual, runnable JS code (usually a massive function with all the logic inside). But raw data? JSON data is not code. There's nothing to run; it's just plain text.
Therefore, there's no way to handle or manipulate our precious data. The browser will download the data pointed at by our <script>
tag and when processing it'll rightfully complain:
wtf is this {"user":"Smith"}
crap we loaded? It's not code. I can't compute, syntax error!
The JSONP hack
The old/hacky way to utilize that data? If we could make plain text somehow runnable, we could grab it on runtime. So we need anotherweb.com
to send it with some logic, so when it's loaded, your code in the browser will be able to use said data. We need two things: 1) to get the data in a way that it can be run, and 2) write some code in the client so that when the data runs, this code is called and we get to use the data.
For 1) we ask the foreign server to send us the JSON data inside a JS function. The data itself is set up as that function's input. It looks like this:
peopleDataJSONP({"user":"Smith"})
which makes it JS code our browser will parse and run without complaining! Exactly like it does with the jQuery library.
To receive the data like that, the client "asks" the JSONP-friendly server for it, usually done like this:
<script src="https://anotherweb.com/api/data-from-people.json?myCallback=peopleDataJSONP"></script>
As per 2), since our browser will receive the JSONP with that function name, we need a function with the same name in our code, like this:
function peopleDataJSONP(data){
alert(data.user); // "Smith"
}
The browser will download the JSONP and run it, which calls our function, where the argument data
will be the JSON data from anotherweb.com
. We can now do with our data whatever we want to.
Don't use JSONP, use CORS
JSONP is a cross-site hack with a few downsides:
- We can only perform GET requests
- Since it's a GET request triggered by a simple script tag, we don't get helpful errors or progress info
- There are also some security concerns, like running in your client JS code that could be changed to a malicious payload
- It only solves the problem with JSON data, but Same-Origin security policy applies to other data (WebFonts, images/video drawn with drawImage()...)
- It's not very elegant nor readable.
The takeaway is that there's no need to use it nowadays.
You should read about CORS here, but the gist of it is:
Cross-Origin Resource Sharing (CORS) is a mechanism that uses
additional HTTP headers to tell browsers to give a web application
running at one origin, access to selected resources from a different
origin. A web application executes a cross-origin HTTP request when it
requests a resource that has a different origin (domain, protocol, or
port) from its own.
- origin is defined by 3 things: protocol, port, and host. So, for example,
https://web.com
is a different origin than http://web.com
(different protocol) and https://web.com:8081
(different port) and obviously https://thatotherweb.net
(different host)