GAE/Python で JSONP

Sever 2015. 3. 27. 16:33
반응형

http://99blues.dyndns.org/blog/2011/07/gae_jsonp/


크로스도메인 해결 방법으로 jsonp 사용 

Google App Engine/Python 上に JSONPのサービスを実装してみます。
また、クライアントからサーバへののJSONP呼び出しは jQueryを使用します。

サンプルを作ってみる

JSONPの概要説明はネットで検索すればいろいろ見つかるので省略し、さっさと実装していきます。

簡単な実装

まずは、固定値を返す最小限の実装を試します。

サーバー側(GAE/Python)
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
28
29
30
31
32
33
34
35
36
37
38
39
40
# -*- coding: utf-8 -*-
import simplejson
import datetime
 
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
 
class JSONPHandler(webapp.RequestHandler):
    def get(self):
        # クライアントに返すデータ
        data = {'id': self.request.get('id'),
                'name': 'your name'}
 
        # 辞書形式をJSON形式に変換
        json = simplejson.dumps(data, ensure_ascii=False)
 
        # callback でラップ
        callback = self.request.get('callback')
        if callback:
            json = '%s(%s)' % (callback, json)           
 
        # コンテンツ形式は javascript
        self.response.headers['Content-Type'] = 'application/javascript; charset=utf-8'
 
        # データの有効期限を設定 (ここは任意)
        expires_date = datetime.datetime.now() + datetime.timedelta(minutes=10)
        self.response.headers["Expires"] = expires_date.strftime("%a, %d %b %Y %H:%M:00 GMT")
        self.response.headers["Cache-Control"] = "public, max-age=%s" % (60*10)
 
        # 応答を返す
        self.response.out.write(json)
 
application = webapp.WSGIApplication([('/get_jsonp', JSONPHandler),
                                     ], debug=True)
 
def main():
    run_wsgi_app(application)
 
if __name__ == "__main__":
    main()

(使い方)

  1. GAE/Pythonのプロジェクトを作成し、このコードを組み込みます。
  2. SDK付属の開発用サーバを使って、プロジェクトを実行します。

(補足)

  • 11~12行目の辞書データを変更すれば、違うデータを返すことが出来ます。
      data = {'id': self.request.get('id'),
              'name': 'your name'}
  • 18~20行目の処理が、JSONPを返すための肝です。 これをしないと、ただの JSONです。
      callback = self.request.get('callback')
      if callback:
        json = '%s(%s)' % (callback, json)
    


クライアント側(HTML+jQuery)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!DOCTYPE html>
<html>
<head>
    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js"></script>
</head>
 
<body>
    <p>id:<span id="id" style="color:red"></span></p>
    <p>name:<span id="name" style="color:blue"></span></p>
 
    <script type="text/javascript">
        $(function(){
            $.getJSON("http://localhost:8080/get_jsonp?id=hoge&callback=?",
                function(data){
                    $('#id').text(data['id']);
                    $('#name').text(data['name']);
                }
            );
        });
    </script>
</body>
</html>

(使い方)

  1. HTMLファイルをローカルフォルダに保存します。
  2. 必要ならサーバのURLを修正します。 (13行目)
        $.getJSON("http://localhost:8080/get_jsonp?id=hoge&callback=?",  

    接続先がローカル環境の開発用サーバであれば、修正不要です。

  3. ブラウザでHTMLファイルを開きます。
    JSONPが成功すれば、ブラウザに次のように表示されます。

(補足)

  • 13~18行目でJSONPリクエストを処理しています。
      $.getJSON("http://localhost:8080/get_jsonp?id=hoge&callback=?",
        function(data){
          $('#id').text(data['id']);
          $('#name').text(data['name']);
        }
      );
    
    1. jQueryの getJSON()関数でJSONPリクエストを処理します。

    2. サーバからレスポンスが返ってくると、コールバック関数:function() が実行されます。
      サーバからのデータ(JSON形式)はコールバック関数の引数(ここでは変数:data)に渡されます。


モデル(エンティティ)をJSONP

次はGAEらしく、モデルクラス(エンティティ)をJSONP形式にして返してみます。

サーバー側(GAE/Python)
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# -*- coding: utf-8 -*-
import simplejson
import datetime
 
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
from google.appengine.ext import db
 
# 今回使用するモデル
class Person(db.Model):
    name = db.StringProperty(required=True)
    age = db.IntegerProperty(required=True)
 
class EntityHandler(webapp.RequestHandler):
    def get(self):
        # モデルのインスタンス
        person = Person(name='hoge hoge', age = 99)
 
        # モデルをPythonの辞書形式に変換
        data = dict(name=person.name, age=person.age)
 
        # 後は最初のサンプルと同じ
        json = simplejson.dumps(data, ensure_ascii=False)
        callback = self.request.get('callback')
        if callback:
            json = '%s(%s)' % (callback, json)           
 
        self.response.headers['Content-Type'] = 'application/javascript; charset=utf-8'
 
        expires_date = datetime.datetime.now() + datetime.timedelta(minutes=10)
        self.response.headers["Expires"] = expires_date.strftime("%a, %d %b %Y %H:%M:00 GMT")
        self.response.headers["Cache-Control"] = "public, max-age=%s" % (60*10)
 
        self.response.out.write(json)
 
application = webapp.WSGIApplication([('/get_entity', EntityHandler),
                                     ], debug=True)
 
def main():
    run_wsgi_app(application)
 
if __name__ == "__main__":
    main()

(補足)

  1. モデルクラスを辞書形式に変換してしまえば、後は最初のサンプルと同じ手順で処理出来ます。

    (変換例)

      data = dict(name=person.name, age=person.age)
    


クライアント側(HTML+JQuery)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!DOCTYPE html>
<html>
<head>
    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js"></script>
</head>
 
<body>
    <p>name:<span id="name" style="color:red"></span></p>
    <p>age:<span id="age" style="color:blue"></span></p>
 
    <script type="text/javascript">
        $(function(){
            $.getJSON("http://localhost:8080/get_entity?callback=?",
                function(data){
                    $('#name').text(data['name']);
                    $('#age').text(data['age']);
                }
            );
        });
    </script>
</body>
</html>

最初の例とあまり変わらないので、説明は省略します。

JSONPではどんなデータが流れているか?

JSONPを使う時、クライアント・サーバ間でどんな内容の通信が行われているのでしょうか?
ブラウザ(Chrome)のデバッグ機能を使って、通信内容を見てみます。

リクエストURI

クライアント側のJSONPリクエストを発行するコードは次のようになってました。

  $.getJSON("http://localhost:8080/get_jsonp?id=hoge&callback=?",
    function(data){
      $('#id').text(data['id']);
      $('#name').text(data['name']);
    }
  );

実際にサーバに送信するときのリクエストURIは次のようになっています。

http://localhost:8080/get_jsonp?id=hoge&callback=jQuery16106045830205548555_1310797398214&_=1310797398220

サーバからのレスポンス

サーバからレスポンスデータは次のようになっています。

単なるJSONデータではなく、関数呼び出しでラッピングされていることが分かります。

最後に

JSONPとか、クロスドメインと聞くと何やら難しげに思えますが、実際はサーバ側、クライアント側共、実装方法はそれほど難しくありません。
クロスドメインが使えるようになれば、システム構成の自由度が上がり便利です。

Comments are closed.

반응형
: