最近の話ですが、このサイトにも広告を載せるようになりました。
Google Adsense の方はタグ貼るだけで終わりの簡単なものなのですが、縁あってレバレジーズ株式会社の広告を載せることになり、これはこのサイトでバナーを供給しています。
このサイトは MovableType + DynamicMTML で運用していますので、普通に考えれば PHP で直接広告タグを書けばいいだけなのですが、サーバー負荷の観点からいまいちだし、第一余りおもしろくありません。
そこで、広告供給サーバーを別に立て、JSONP で供給するようにしてみました。
JSONP とは
JSONP とは“JSON with Padding”の略で……と書くよりも Wikipedia 見てもらった方が確実ですね。
JSONP – Wikipedia
http://ja.wikipedia.org/wiki/JSONPJSONP(JSON with padding)とは、scriptタグを使用してクロスドメインなデータを取得する仕組みのことである。
今回作った画像供給サーバーは img.remora.cx というドメインにあり、このブログのドメイン blog.delphinus.dev とは別の場所にあります。
広告は Ajax を使って動的に取得したいのですが、同一生成元ポリシーにより別ドメインへの XMLHttpRequest は許可されていません。
ここが危ない!Web2.0のセキュリティ:第2回 Same-Originポリシーと迂回技術|gihyo.jp … 技術評論社
http://gihyo.jp/dev/serial/01/web20sec/0002
これを回避するにはリバースプロクシを使ったり Flash を使ったり……という方法があるのですが、この中で一番簡単に実装できそうなのが、JSONP を使った方法です。
ソースコード
と言うわけで、書いたコードはこれ。
delphinus35/BannerServer · GitHub
https://github.com/delphinus35/BannerServer
サーバー部分
実装には Amon2::Lite を使っています。長々と書いてありますが、簡単にすると次のようになるでしょうか。
use Amon2::Lite;
get '/get' => sub { my $c = shift;
my $p = $c->req->parameters;
# HTML を作成して JSON 化
my $html = $c->create_view->render('img.tt', $p);
my $js = to_json(+{content => $html});
# callback が指定してあればそれでラップ
$js = "$p->{callback}($js);" if defined $p->{callback};
return $c->create_response(
200,
[
'Content-Type' => 'application/json',
'Content-Length' => length $js,
],
$js,
);
};
get '/img/{filename}' => sub { my ($c, $args) = @_;
# $args->{filename} にファイル名が入っているので、
# その内容を $content に読む
return $c->create_response(
200,
[
'Content-Type' => "image/$ext",
'Content-Length' => -s $args->{filename},
],
$content,
);
};
__DATA__
@@ img.tt
<a target="_blank" href="[% path %]">
<img src="http://img.example.com[% uri_for('/img/' _ filename) %]">
</a>
クライアント部分
これに対してクライアント部分は次のようになります。今回作成したもののうち、ポイントとなる部分を抜き出すと次のようになります。
$.getJSON('http://img.remora.cx/get?callback=?', {},
function(data) {
$this.html(data.content);
}
);
ここでは記述を簡単にするため、jQuery の getJSON() メソッドを使っています。
jQuery.getJSON() – jQuery API
http://api.jquery.com/jQuery.getJSON/jQuery.getJSON( url [, data] [, success(data, textStatus, jqXHR)] )
url: リクエストが送られる URL
data: リクエストパラメータ
success(data, textStatus, jqXHR): 成功時に実行される関数
このメソッド自体は単にサーバーから JSON を得るだけなのですが、URL に ?callback=? という文字列を付加すると JSONP が使えるようになります。
上に書いた Javascript を実行すると次のような感じで処理されます。(実際の処理を簡略化しています。)
- URL の最後の
?がランダムな文字列に置き換えられ、サーバーにリクエストされる。http://img.remora.cx/get?callback=jQuery12345_67890
- サーバーは JSON を生成し、それを
callbackパラメータの文字列で包んで返す。jQuery12345_67890({"content":"<p>aiueo!</p>"}); - クライアントは
getJSON()の第三パラメータ(success())を上記ランダム文字列の変数に代入している。var jQuery12345_567890 = function(data){ $('#aaa').html(data.content); }; - サーバーから持ち帰った JSON を元にして
<script>タグが生成、実行される。// こんな HTML がページに挿入される <script> var jQuery12345_67890 = function(data){ $('#aaa').html(data.content); }; jQuery12345_67890({"content":"<p>aiueo!</p>"}); </script>
形としては外部のサーバーから読み込んだスクリプトを実行しているだけなのに、処理自体はリクエストに従ってサーバーで動的に作られるのです。うまいこと考えたもんですね。
丁度この文に下に現れているのが広告です。うまく表示されているかな?
