JavaScript: How do I create JSONP?

47,621

Solution 1

It is simple. Simply accept a parameter called callback in the GET.

Then wrap the callback JavaScript function around your data.

Example in PHP:

<?php

$data = '{}'; // json string

if(array_key_exists('callback', $_GET)){

    header('Content-Type: text/javascript; charset=utf8');
    header('Access-Control-Allow-Origin: http://www.example.com/');
    header('Access-Control-Max-Age: 3628800');
    header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE');

    $callback = $_GET['callback'];
    echo $callback.'('.$data.');';

}else{
    // normal JSON string
    header('Content-Type: application/json; charset=utf8');

    echo $data;
}

It's idea is to simply return a JavaScript file which calls the callback function with the JSON object as the first parameter of the JavaScript callback function.

You can use the built-in json_encode() function to create JSON strings (which $data in our example above contains) from arrays and objects in PHP.

To use the JSONP service, you can use the <script> tag:

<script>
    function receiver(data){
        console.log(data);
    }
</script>
<script src="data-service.php?callback=receiver"></script>

Solution 2

You need a server-side language, the callback parameter is simply a GET parameter, you read the param, and you wrap the JSON response into a function call and you print it like this callback(jsonResponse);.

I leave you a really minimalist example using Python since you don't mention any server-side language:

import os
import cgi

form = cgi.FieldStorage()
callback = form.getvalue('callback','')

address = cgi.escape(os.environ["REMOTE_ADDR"])

json = '{"ip": "'+address+'", "address":"'+address+'"}'

#Allow cross domain XHR
print 'Access-Control-Allow-Origin: *'
print 'Access-Control-Allow-Methods: GET'

if callback != '':
  print 'Content-Type: application/javascript'
  result = callback+'('+json+');'
else:
  print 'Content-Type: application/json'
  result = json

print ''
print result

That is the code of a small JSONP service used to retrieve the client IP address made by Zach and it is hosted on the Google App Engine.

Solution 3

Mauris already gave you a working example. I would only add that you should check if a callback param is present and non-empty, and if not, return the json data as is without the parentheses. So basically your api will be JSON with provision of being JSON-P when callback is given.

To consume the JSON-P webservice, unless you use a framework like YUI or jQuery, you can simply create a script node dynamically and set its src attribute to point to the webservice. Remember to remove the node from the dom before repeating it again, since this dynamic script node is single use only.

Solution 4

I know I'm late to the party, and there was a comment about security of the code in one of the answers. Here is a good article about this:

http://www.geekality.net/2010/06/27/php-how-to-easily-provide-json-and-jsonp/

And here is the code that you should be running:

<?php header('content-type: application/json; charset=utf-8');

function is_valid_callback($subject)
{
    $identifier_syntax
      = '/^[$_\p{L}][$_\p{L}\p{Mn}\p{Mc}\p{Nd}\p{Pc}\x{200C}\x{200D}]*+$/u';

    $reserved_words = array('break', 'do', 'instanceof', 'typeof', 'case',
      'else', 'new', 'var', 'catch', 'finally', 'return', 'void', 'continue', 
      'for', 'switch', 'while', 'debugger', 'function', 'this', 'with', 
      'default', 'if', 'throw', 'delete', 'in', 'try', 'class', 'enum', 
      'extends', 'super', 'const', 'export', 'import', 'implements', 'let', 
      'private', 'public', 'yield', 'interface', 'package', 'protected', 
      'static', 'null', 'true', 'false');

    return preg_match($identifier_syntax, $subject)
        && ! in_array(mb_strtolower($subject, 'UTF-8'), $reserved_words);
}

$data = array(1, 2, 3, 4, 5, 6, 7, 8, 9);
$json = json_encode($data);

# JSON if no callback
if( ! isset($_GET['callback']))
    exit($json);

# JSONP if valid callback
if(is_valid_callback($_GET['callback']))
    exit("{$_GET['callback']}($json)");

# Otherwise, bad request
header('status: 400 Bad Request', true, 400);

Solution 5

// Adds script tag to head of the page
function addScriptToHead(source, code, type) {
    var script = document.createElement('script');
    if (type === 'js') {
        script.setAttribute('type', 'text/javascript');
    }
    if (source !== '') {
        script.setAttribute('src', source);
    }
    if (code !== '') {
        if (document.all && !window.opera)  {
            script.text = code;
        } else {
            script.innerHTML = code;
        }
    }
    document.getElementsByTagName('head')[0].appendChild(script);
}


// Callback function
function addScriptToHead(any_param) {

// do whatever needs to be done

}

//call example

addScriptToHead('http://url_to_receiver_script/index.php&param=anything', '', 'js');

/// the callback script should return name of the Callback function, i.e. if you type in browser

http://url_to_receiver_script/index.php&param=anything

it should return just a text (name of existing processing function): addScriptToHead(any_param)

works like a clock in any browser.

Share:
47,621
Teddi
Author by

Teddi

Updated on July 19, 2022

Comments

  • Teddi
    Teddi almost 2 years

    I have a two domains, example1.com and example2.com

    From example1.com, I would like call a JSON API I have on example2.com. Knowing that this is not allowed, it occurred to me - this is exactly why JSONP was created.

    Question is, how do I modify my JSON API to make it JSONP capable?

    Basically, how do I create the callback api?

    UPDATE

    My server side language is PHP

  • Teddi
    Teddi over 14 years
    Wow, is it really just that simply?
  • Teddi
    Teddi over 14 years
    Do I need to add: echo 'Access-Control-Allow-Origin: *' echo 'Access-Control-Allow-Methods: GET' ???
  • Christian C. Salvadó
    Christian C. Salvadó over 14 years
    @Teddi: Those headers are to support the upcoming XMLHttpRequest 2 specification (w3.org/TR/access-control) and BTW they already work on Firefox 3.5 (developer.mozilla.org/en/HTTP_access_control)
  • Teddi
    Teddi over 14 years
    @Mauris, would you mind updating your code to include the new XMLHttpRequest v2 spec headers. thanks
  • mauris
    mauris over 14 years
    @Teddi - it's not that important because it's upcoming. It'll work with the script tag hack. Anyway I've added the headers in.
  • Kent Brewster
    Kent Brewster over 14 years
    Please be sure to filter your callback parameter so only alpha, numeric, dot, and square brackets are valid characters. Otherwise you're stepping into a world of hurt.
  • SnareChops
    SnareChops over 11 years
    @KentBrewster How would one do that?
  • Bob Vork
    Bob Vork about 10 years
    If by chance anyone is using angular, this won't work. Angular supports jsonp, but replaces the callback with something like angular.callback._0. Would allowing a callback like this be unsafe?
  • Joel Kinzel
    Joel Kinzel about 10 years
    @BobVork I don't believe so, you can change the $identifier_syntax to include periods as well. In fact, I believe I ended up doing that in my implementation for that very reason (but posted this before I had finished my implementation).
  • Sverrir Sigmundarson
    Sverrir Sigmundarson almost 10 years
    @SnareChops, this is one way to clear out unwanted characters from the callback stackoverflow.com/a/10900911/1082542