JSONP を使ってごにょごにょしたかったので、そのやり方をまとめる。
環境
- Node v0.10.17
- Express v3.4.8
- npm v1.3.8
概要
自前のJSONP サーバを作って静的なHTMLコンテンツに反映させたいというとき。 よくマッシュアップと俗にいわれる外部APIとかでJSONしか返さないものだったりした場合、それを取りにいっても Same Origin Policy でAjax でデータを取って来れない。それを解消するには自前でJSONを取ってくるサーバを用意してJSONPを返すようにしてあげる必要がある。そうすれば、別ドメインでも静的HTMLが動く。
やりたいことを簡単に説明するとこんな感じ
3はJSONでやり取りしているのに対し、4はJSONPでやるようにする。 JSONP とは何者なのか。 なぜJSONP にすれば違うホストでもAjax通信ができるのか。
JSONP
実はJSONPはAjax通信ではない。 < script>タグのsrcに対象のURLを入れているだけだ。 src属性は違うホストでも読み込んでくれるので、scriptタグをJavaScriptで動的に作り、HTMLに追加することで以下のようになる。
<script type="text/javascript" src="http://myhost.com/?callback=hoge¶m1=a"/>
これを読み込みにいくとこんな結果が帰ってくるようにする。
hoge({items: [{item: "item1"}, {item: "item2"]})
そうするとNodeで作ったサーバが結局はJSONで欲しかった上図の3を取って来きてくれるようになるのだ。
Express によるサンプル
app.js
var express = require("express"); var app = express(); var http = require("http"); var https = require("https"); var URL = require('url'); getJSON = function(options, onResult) { var prot = options.port == 443 ? https : http; var req = prot.request(options, function(res) { var output = ''; res.setEncoding('utf8'); res.on('data', function (chunk) { output += chunk; }); res.on('end', function() { onResult(res, output); }); }); req.on('error', function(err) { res.send('error: ' + err.message); }); req.end(); }; // Configuration app.configure(function(){ app.set('views', __dirname + '/views'); app.use(express.bodyParser()); app.use(express.methodOverride()); app.use(app.router); app.use(express.static(__dirname + '/public')); }); app.configure('development', function(){ app.use(express.errorHandler({ dumpExceptions: true, showStack: true })); }); app.configure('production', function(){ app.use(express.errorHandler()); }); app.get('/', function(request, response){ response.contentType('application/json'); var query = URL.parse(request.url, true).query; var itemNumber; var callback; var resData; callback = query['callback']; param = query['param']; options = { host: 'request_host, port: '80', path: '/search/?q=' + param, method: 'GET', } getJSON(options, function(res, result){ response.statusCode = 200; response.send(callback + '(' + result + ')'); }); }); app.listen(3000);
大事なのは下から5行目あたり。esponse.send(callback + '(' + result + ')');
でとってきたJSONをコールバック関数名で()でくくってある。これでさっきのscriptタグの中身を実現しているという訳だ。
軽くまとめ
HTMLだけで外部のAPIで商品や動画などの検索したりしたい場合、JSONPじゃないと通信できない
JSON やXMLのみでしか外部APIを提供していないサービスの場合、それを裏で取ってくるサーバが必要。
そのサーバがJSONPを返すようにする、もしくは動的にページを作り替えるWebフレームワークを使うことでこの問題は解決される。今回は前者の場合の対応を解説した。
当たり前だけど、上のサンプルをローカルで動かしても意味ないので、どっかのサーバに上げる必要がある。 Heroku なら簡単にできるのでそこはググって頑張ってください。
※サンプルは元ソースからブログ用に改変した部分があるので動かない可能性あり。