ボクココ

個人開発に関するテックブログ

JSONP とは何か。 Node.js と express をサンプルにして解説

JSONP を使ってごにょごにょしたかったので、そのやり方をまとめる。

環境

  • Node v0.10.17
  • Express v3.4.8
  • npm v1.3.8

概要

自前のJSONP サーバを作って静的なHTMLコンテンツに反映させたいというとき。 よくマッシュアップと俗にいわれる外部APIとかでJSONしか返さないものだったりした場合、それを取りにいっても Same Origin Policy でAjax でデータを取って来れない。それを解消するには自前でJSONを取ってくるサーバを用意してJSONPを返すようにしてあげる必要がある。そうすれば、別ドメインでも静的HTMLが動く。

やりたいことを簡単に説明するとこんな感じ

f:id:cevid_cpp:20140224233956p:plain

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&param1=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 なら簡単にできるのでそこはググって頑張ってください。

※サンプルは元ソースからブログ用に改変した部分があるので動かない可能性あり。