2006年09月18日
川俣晶の縁側ソフトウェア技術雑記total 10699 count

英文ブログ記事"Remote JSON - JSONP"の解説……というか解析結果?

Written By: 川俣 晶連絡先

 今更という話ではありますが。

 JSONPを正しく理解しようと思って、Remote JSON - JSONPを読んだところ、全く意味が理解できず、敗退しました。

 というわけで、手間を惜しんだら負けなのかと思い、いい加減で当てにならない日本語訳(後述)をやってみました。しかし、それでも分かりません。

 いろいろ苦闘してやっと理解しました。

 結論としては、「ソースコードの断片しか提示していないので、全体像が見えないとコードを追えない」「del.icio.us JSON APIの理解を前提としている」「(英文が難しくて読み取れないよ)」という2つ(3つ)の問題が理解を妨げていたことが分かりました。

 一応、いい加減な和訳は最後に付けるとして、理解した内容を簡単にまとめておきます。サンプルソースは、原文のものをそのまま使います。

 間違い等あれば、ご教示ください。

JSONPってざっくばらんに何? §

 JSONデータに、若干の文字(コード)を追加したものです。追加がpaddingで、JSON+Padding=JSONPという感じでしょう。

 なぜ追加するのかというと、XMLHttpRequestオブジェクトで受け取ってeval関数で評価するのではなく、HTMLのscript要素で読み込ませたいからです。それによって、別ドメインのデータを持って来ることができます。

 具体例を見てみましょう。

サンプルソース §

 たとえば、JSONがこうだとします。(サーバからクライアントに送信される内容)

{

    "u": "http://mochikit.com/examples/interpreter/index.html",

    "d": "Interpreter - JavaScript Interactive Interpreter",

    "t": [

        "mochikit","webdev","tool","tools",

        "javascript","interactive","interpreter","repl"

    ]

}

 しかし、この内容はscript要素では上手く受け取れません。

 そこで、受け取るために前後に若干の文字を付けて以下のようにします。(Deliciousを使った一例)(サーバからクライアントに送信される内容)

if(typeof(Delicious) == 'undefined') Delicious = {};

Delicious.posts = [{

    "u": "http://mochikit.com/examples/interpreter/index.html",

    "d": "Interpreter - JavaScript Interactive Interpreter",

    "t": [

        "mochikit","webdev","tool","tools",

        "javascript","interactive","interpreter","repl"

    ]

}]

 上記の例なら、Delicious.postsで読み込んだデータを参照できます。

いつ読み込みが完了したのか分からないぞ! §

 ここまではさほど難しい話ではありません。

 難しくなるのは、動的にscript要素を使ってJSONPオブジェクトを要求し、それを受信した場合です。この場合、いつ受信が完了したのか、知る方法がありません。

解決策 §

 JSONでは、ストイックにデータに限定して関数などを含めることを許していません。(そうしないと危険だし、他プログラム言語での実装が困難になる)

 しかし、既にオブジェクトの生成や代入文を付け足してしまった以上、ストイックさを維持する意味はありません。既に、サーバから戻ってくるデータとは、プログラムそのものなのです。

 ならば、転送完了を通知する関数呼び出しのコードを、サーバが返送するデータの一部に含めてしまえばよいのです。

 以下は、クライアント側で実行するコールバック付きのサーバ呼び出しコードです。(ただし内容は不完全です。実際にscript要素をいじるコードなど重要な部分が欠落しています)

var delicious_callbacks = {};

function getDelicious(callback, url) {

    var uid = (new Date()).getTime();

    delicious_callbacks[uid] = function () {

        delete delicious_callbacks[uid];

        callback();

    };

    url += "?jsonp=" + encodeURIComponent("delicious_callbacks[" + uid + "]");

    // add the script tag to the document, cross fingers

};

getDelicious(doSomething, "http://del.icio.us/feeds/json/bob/mochikit+interpreter");

 このgetDelicious関数は、第1引数にコールバックする関数を指定し、第2引数に呼び出すURLを指定します。そして、URLからの読み込みが完了したとき、指定されたコールバック関数が呼び出されます。複数の呼び出しが同時並行で行われても良いように、現在の日付時刻をID(uid)としてコールバック関数を保存しておく仕掛けです。

 このとき、サーバから返送されるコードは以下のような関数呼び出しコードになります。ちなみに、12345の部分は動的に変化するuidに対応する部分です。

delicious_callbacks[12345]([{

    "u": "http://mochikit.com/examples/interpreter/index.html",

    "d": "Interpreter - JavaScript Interactive Interpreter",

    "t": [

        "mochikit","webdev","tool","tools",

        "javascript","interactive","interpreter","repl"

    ]

}])

 動的なscript要素でこれを読み込むと、サーバからの返送値となるオブジェクトを引数として、コールバック関数の呼び出しが発生します。

これにより達成したものは何か §

  • 別ドメインのサーバが持つデータの動的な取得
  • 読み込み完了の通知
  • 標準化された動作

感想 §

 私が個人的に思ったことです。

  • uidの作成ロジックは本当に妥当か? 1クライアント内でユニークであれば良いのなら、この程度でも良いのか?
  • サーバが変なコードを挿入してきたら恐いかも。別ドメインのサーバを呼ぶということは、他人が管理するサーバを利用するということなので、リスクがあるかも

いい加減で当てにならない日本語訳 §

The browser security model dictates that XMLHttpRequest, frames, etc. must have the same domain in order to communicate. That's not a terrible idea, for security reasons, but it sure does make distributed (service oriented, mash-up, whatever it's called this week) web development suck.

ブラウザのセキュリティモデルは、同じドメインと通信を持たねばならないことを、XMLHttpRequest、フレーム、等々に命令する。

セキュリティの理由から、それは恐ろしいアイデアではないが、……(訳注:訳せないが重要ではないと判断して訳さない)

There are traditionally three solutions to solving this problem.

この問題には、伝統的に3つの解決策がある。

Local proxy:

Needs infrastructure (can't run a serverless client) and you get double-taxed on bandwidth and latency (remote - proxy - client).

インフラが必要(サーバ抜きのクライアントでは走らない)で、2重にバンド幅とレイテンシーを使う

Flash:

Remote host needs to deploy a crossdomain.xml file, Flash is relatively proprietary and opaque to use, requires learning a one-off moving target programming langage.

リモートホストは、crossdomain.xmlファイルを配置する必要がある。Flashは相対的に独占的であり、使うには不透明。1回限り動くターゲットのプログラミング言語を習う必要がある。

Script tag:

Difficult to know when the content is available, no standard methodology, can be considered a "security risk".

コンテントが利用可能になったことを知るのが難しく、標準の方法論もなく、セキュリティのリスクとして見なされる。

--------------------------------------------------------------------------------

I'm proposing a new technology agnostic standard methodology for the script tag method for cross-domain data fetching: JSON with Padding, or simply JSONP.

わたしは新しい技術を提案する。ドメインをまたがるデータ取得のためのscriptタグメソッドのための不可知論の標準方法論である。JSON with Padding、あるいはシンプルにJSONPである。

The way JSONP works is simple, but requires a little bit of server-side cooperation. Basically, the idea is that you let the client decide on a small chunk of arbitrary text to prepend to the JSON document, and you wrap it in parentheses to create a valid JavaScript document (and possibly a valid function call).

JSONPの働きはシンプル。しかし、僅かなサーバ側の協力が必要とされる。

基本的に、このアイデアは、前もって考えたJSON文書とする任意のテキストの小さなチャンク上で、あなたがクライアントを決定する。

そして、合法的なJavaScript文書を生成するために、あなたはそれを括弧でラップする。(そして可能なら、合法な関数呼び出しとする)

The client decides on the arbitrary prepended text by using a query argument named jsonp with the text to prepend. Simple! With an empty jsonp argument, the result document is simply JSON wrapped in parentheses.

前もって考えられたテキストと共に、jsonpで名前づけられたクエリ引数によって、クライアントは任意の前もって考えられたテキストを決定する。

シンプル!

空のjsonp引数と共に、括弧の中で、結果文書はシンプルなJSONにラップされている。

Let's take the del.icio.us JSON API as an example. This API has a "script tag" variant that looks like this:

del.icio.us JSON APIを1つの事例としてとりあげよう。

このAPIは、以下のような"script tag"の変種を持っている。

http://del.icio.us/feeds/json/bob/mochikit+interpreter:

if(typeof(Delicious) == 'undefined') Delicious = {};

Delicious.posts = [{

    "u": "http://mochikit.com/examples/interpreter/index.html",

    "d": "Interpreter - JavaScript Interactive Interpreter",

    "t": [

        "mochikit","webdev","tool","tools",

        "javascript","interactive","interpreter","repl"

    ]

}]

In terms of JSONP, a document semantically identical to this would be available at the following URL:

JSONPの点から見ると、意味的に等価に文書は以下のURLとして得られるだろう。

http://del.icio.us/feeds/json/bob/mochikit+interpreter?jsonp=if(typeof(Delicious)%3D%3D%27undefined%27)Delicious%3D%7B%7D%3BDelicious.posts%3D

That's not very interesting on its own, but let's say I wanted to be notified when the document is available. I could come up with a little system for tracking them:

それ自身、とても興味深いものではない。

しかし、言わせてほしい。私はいつドキュメントが有効になったのか通知したかった。

私は、それをトラッキングする小さなシステムを仕上げることができた。

var delicious_callbacks = {};

function getDelicious(callback, url) {

    var uid = (new Date()).getTime();

    delicious_callbacks[uid] = function () {

        delete delicious_callbacks[uid];

        callback();

    };

    url += "?jsonp=" + encodeURIComponent("delicious_callbacks[" + uid + "]");

    // add the script tag to the document, cross fingers

};

getDelicious(doSomething, "http://del.icio.us/feeds/json/bob/mochikit+interpreter");

The fetched URL from this hypothetical experiment would look something like this:

仮説の実験から来たフェッチURLは以下のように見えるだろう。

http://del.icio.us/feeds/json/bob/mochikit+interpreter?jsonp=delicious_callbacks%5B12345%5D

delicious_callbacks[12345]([{

    "u": "http://mochikit.com/examples/interpreter/index.html",

    "d": "Interpreter - JavaScript Interactive Interpreter",

    "t": [

        "mochikit","webdev","tool","tools",

        "javascript","interactive","interpreter","repl"

    ]

}])

See, because we're wrapping with parentheses, a JSONP request can translate into a function call or a plain old JSON literal. All the server needs to do differently is prepend a little bit of text to the beginning and wrap the JSON in parentheses!

見よ、我々は括弧でラップしたために、1つのJSONPリクエストは関数呼び出しに変換でき、あるいはプレーンな古いJSONリテラルでもある。

(2006年9月19日差し替え)(この文は意味が読み取れなかったが、thatを補ってAll that the server needs to do……とすれば意味を読み取れるとメールでご教示頂いた。そこで訳文を差し替えてみた) サーバが異なる動作を行う必要のある全てのことは、開始部分の事前に考えられた小さなテキストとJSONをラップする括弧である。(注: サーバがJSONで送る場合と比較して変更しなければならないのは、JSONデータに括弧を付け、先頭に若干のコードを付けることだけだ……と言っていると推定)

Now, of course, you'd have libraries like MochiKit, Dojo, etc. abstracting JSONP so that you don't have to write the ugly DOM script tag insertion yourself, etc.

今、もちろん、あなたはMochiKitやDojoや他の、JSONPを仮想化するライブラリを既に持っている。そこで、あなたは醜いDOMスクリプトタグ挿入をあなた自身で書く必要はない。等々。

Of course, this just solves the standardization problem. Your page is still toast if the remote host decides to inject malicious code instead of JSON data. However, if implemented, it'd save a lot of developers some time and allow for generic abstractions, tutorials, and documentation to be built.

もちろん、これは標準化の問題を解決する。もし、リモートホストがJSONデータではなく悪意あるコードの挿入を決断したら、あなたのページは依然としてtoastである。

しかしながら、もし実装されていたら、それは多くの開発者のいくばかの時間を節約するだろう。そして、ビルドするための汎用的な仮想化、チュートリアル、文書化が可能になるだろう。

Facebook

このコンテンツを書いた川俣 晶へメッセージを送る

[メッセージ送信フォームを利用する]

メッセージ送信フォームを利用することで、川俣 晶に対してメッセージを送ることができます。

この機能は、100%確実に川俣 晶へメッセージを伝達するものではなく、また、確実に川俣 晶よりの返事を得られるものではないことにご注意ください。

このコンテンツへトラックバックするためのURL

https://mag.autumn.org/tb.aspx/20060918174853
サイトの表紙【技術雑記】の表紙【技術雑記】のコンテンツ全リスト 【技術雑記】の入手全リスト 【技術雑記】のRSS1.0形式の情報このサイトの全キーワードリスト 印刷用ページ

管理者: 川俣 晶連絡先

Powered by MagSite2 Version 0.36 (Alpha-Test) Copyright (c) 2004-2021 Pie Dey.Co.,Ltd.