JSONPを用いてクロスドメインで情報を取得する
この辺を読んで、賢いなぁと関心した。せっかくなので、早速やってみた。
ネタはたまたま作ってあったGoogle多数決で、あれはjsonのインターフェイスがあるので、これをちょっとだけ拡張する。最初にでき上がったものから→Not found
まずはバックエンド。
return '('.JSON::Syck::Dump($struct).')';
こういうのを以下のように。
my $result = '('.JSON::Syck::Dump($struct).')'; $result = $self->query->param('jsonp') . $result if $self->query->param('jsonp'); return $result;
実際にはちょっとだけサニタイズとかをしているのでもうちょっと複雑だけれど。
で。JavaScriptのほうもそれっぽく。
submit: function(event){ ... new Ajax.Request( this.form.action, { method: 'post', parameters: Form.serialize(this.form) + '&mode=json', onSuccess: this.succeed.bind(this), onFailure: this.failed.bind(this) } ); ... },
Ajax.Requestな部分を、
submit: function(event){ ... this.jsr = new JSONscriptRequest( 'http://aql.osx.cc/GoogleMajority/?mode=json&jsonp=GM.succeed&' + Form.serialize(this.form) ); this.jsr.buildScriptTag(); this.jsr.addScriptTag(); ... },
scriptタグの挿入に変更したり、
succeed: function(transport){ var json = eval(transport.responseText); this.appendResult(json); ... },
jsonを取る部分を、
succeed: function(json){ this.appendResult(json); ... this.jsr.removeScriptTag(); },
ちょっと書き換えたり、
var GM; Event.observe( window, 'load', function(){ GM = new GoogleMajority({ form: 'form', table: 'result', working: 'working', message: 'message', oddClassName: 'odd', evenClassName: 'even' }); });
上手く受け取れるように、インスタンスを保持するようにしたりした。
もちろんポイントは呼び出し時点の
'http://aql.osx.cc/GoogleMajority/?mode=json&jsonp=GM.succeed&' + Form.serialize(this.form)
のところで、jsonpのキーで渡しているGM.succeedという関数を実行するスクリプトが、相手から帰ってくる。インスタンスを保持しGM.succeedが実行可能にしておきさえすれば、あとはAjax.Requestの時とほとんど変わりない。
jsonをクロスドメインで渡したい場面と言うのはそれなりにあるけれど、アイデア次第で簡単に実現出来るんだなーと思った。例えば、mac.comはサーバサイドプログラムとかは置けないけれど、そういう場合でもこの手法を使えば結構自由度が広がる。とかとか。