JSONP on Google App Engine Python


There are many explanations about JSONP on internet, but a complete sample code for JSONP on Google App Engine for Python is very rare. After trial and error, I finally made it work on Google App Engine for Python. To put JSONP in short, it is a mechanism to make a cross-domain request, which is usually denied by browsers because of security concerns. The technique behind JSONP is simple: use HTML script tag to make HTTP GET request, as if the client is requesting an external JavaScript file. A callback function must be provided to handle the response data from server. For more information, please refer to [1] and [2].

The following is complete sample code.

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 on Google App Engine 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
/* send data to GAE Python server */
function jsonp() {
  var input1value = document.getElementById('input1').value;
  var input2value = document.getElementById('input2').value;
  var url = '/jsonp?callback=mycallback' +
           '&input1=' + encodeURIComponent(input1value) +
           '&input2=' + encodeURIComponent(input2value);
  var ext = document.createElement('script');
  ext.setAttribute('src', url);
  document.getElementsByTagName("head")[0].appendChild(ext);
}

/* receive JSON data from GAE Python server */
function mycallback(JSONdata) {
  /* In order to parse data, we have to know the structure of data from server in advance */

  /* show returned data */
  var infoElm = document.getElementById('info');
  infoElm.innerHTML = 'input1: ' + JSONdata[0]['input1'] + '<br />';
  infoElm.innerHTML += 'input2: ' + JSONdata[1]['input2'] + '<br />';
  infoElm.innerHTML += JSONdata[2] + '<br />';
  infoElm.innerHTML += JSONdata[3] + '<br />';
}

Note

The name of callback function, mycallback in this case, must be consistent. To process the response data from server in the callback function, web application designer must know the structure of the response JSON data in advance.

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

The following code in JSONPPage class:

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

The JSON data is inside parentheses after the name of callback function.

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-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

If you want to use JSONP with anonymous callback function, see [4].

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

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


References:

[1]JSONP Example | Online Solutions Development
[2]Cross Domain Web Mashups with JQuery and Google App Engine
[3]Google App Engine for Python
[4]JSONP with Anonymous Callback Function
[5]Use Object Instance Function as JSONP Callback Function