JSONP with Anonymous Callback Function


In my previous post [1], a complete JSONP example on both client side (browser) and server side (Google App Engine Python) is shown. The example in previous post needs a named, JavaScript callback function to handle the response returned from server. In this post, instead of using named callback function, an alternative choice is shown: use an anonymous callback function in JSONP request.

The main difference between named callback function and anonymous callback function is that in named callback function, only the name of callback function is supplied in the HTTP request, while in anonymous callback function, the whole callback function is supplied in the HTTP request.

The following is a complete example of JSONP with anonymous callback function.

index.html (run on client side, i.e., browser):

index.html | repository | view raw
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <title>JSONP with Anonymous Callback Function on GAE Python example</title>
</head>
<body>

<br style="line-height: 3em;" />
<form style="text-align: center;" action="javascript:jsonp();">
  <input id="input1" type="text" value=""><br />
  <input id="input2" type="text" value=""><br />
  <input type="submit" value="Submit">
</form>
<br style="line-height: 3em;" />
<div id="info" style="text-align: center;">
   try to input something and then submit.
   a JSONP request will be sent to Google App Engine Python server
   and then response will be sent back.
</div>

<script src="jsonp.js"></script>
</body>
</html>

jsonp.js (run on client side, i.e., browser):

jsonp.js | repository | view raw
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/* send data to GAE Python server */
function jsonp() {
  var input1value = document.getElementById('input1').value;
  var input2value = document.getElementById('input2').value;

  // anonymous callback function to process JSON data returned from server.
  var callback = function(JSONdata) {
    /* In order to parse data, we have to know the structure of data from server in advance */
    /* show data returned from server */
    var infoElem = document.getElementById('info');
    infoElem.innerHTML  = 'input1: ' + JSONdata[0]['input1'] + '<br />';
    infoElem.innerHTML += 'input2: ' + JSONdata[1]['input2'] + '<br />';
    infoElem.innerHTML += JSONdata[2] + '<br />';
    infoElem.innerHTML += JSONdata[3] + '<br />';
  };

  var url = '/jsonp?callback=' + encodeURIComponent(callback.toString()) +
           '&input1=' + encodeURIComponent(input1value) +
           '&input2=' + encodeURIComponent(input2value);

  var ext = document.createElement('script');
  ext.setAttribute('src', url);
  document.getElementsByTagName("head")[0].appendChild(ext);
}

Note

In '/jsonp?callback=' + encodeURIComponent(callback.toString()), the whole anonymous callback function is supplied in the HTTP request.

jsonp.py (run on server side, i.e., GAE Python):

jsonp.py | repository | view raw
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#!/usr/bin/env python
# -*- coding:utf-8 -*-

import webapp2
import urllib2
import json


class JSONPPage(webapp2.RequestHandler):
  def get(self):
    input1 = urllib2.unquote(self.request.get('input1'))
    input2 = urllib2.unquote(self.request.get('input2'))
    result = [{ 'input1': input1 },
              { 'input2': input2 },
              ('message #1', 'from', 'server'),
              ('message #2', 'from', 'server')]
    self.response.headers['Content-Type'] = 'application/javascript'
    self.response.out.write(
        "(%s)(%s);" %
        (urllib2.unquote(self.request.get('callback')),
        json.dumps(result))
    )


application = webapp2.WSGIApplication([
    ('/jsonp', JSONPPage),
], debug=True)

Note

In the code:

self.response.out.write(
    "(%s)(%s);" %
    (urllib2.unquote(self.request.get('callback')),
    json.dumps(result))
)

The anonymous callback function is put inside parentheses. Without the parentheses, error will occur while executing the function on client. Compared with example in previous post [1], the named callback function does not need to be inside parentheses. This is another difference between named and anonymous callback function.

Also do not forget the semicolon in (%s)(%s);

app.yaml (on server side, GAE Python config file):

app.yaml | repository | view raw
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
application: jsonp-anonymous-callback-function-gae-python
version: 1
runtime: python27
api_version: 1
threadsafe: true

handlers:
- url: /
  static_files: index.html
  upload: index.html

- url: /jsonp.js
  static_files: jsonp.js
  upload: jsonp.js

- url: /jsonp
  script: jsonp.application

Caveat

The anonymous function is passed as argument in the URL. If the anonymous function is too long, this may exceed the length limit of URI, i.e., the server will return HTTP 414 error (Requested URI too long). As a result, if a callback function is so long such that it exceeds URI length limit, the only choice is to use named callback function.


If you want to use JSONP with object instance function as callback function, see [2].

Tested on: Ubuntu Linux 14.10, Google App Engine Python SDK 1.9.18


References:

[1](1, 2) JSONP on Google App Engine Python
[2]Use Object Instance Function as JSONP Callback Function