クロスサイトリクエストの作成
この時点で、クライアントサイドのコードで株価データをシミュレートするストックウォッチャー(株価監視)アプリケーションの最初の実装の修正をしています。現在の実装ではローカルサーバからJSONフォーマット化されたデータを取得しています。
このセッションでは代わりにリモートサーバを呼び出します。そうするには、SOP (Same Origin Policy)の制約を回避する必要があります。
Note: GWTアプリケーションにおけるクライアント-サーバ間通信についての幅広いガイドはCommunicate with a Serverを見てください。
始める前に
StockWatcher プロジェクト
このチュートリアルはGWTの概念とBuild a Sample GWT Application チュートリアルで作成した
StockWatcherアプリケーションを基にしています。 もしGWTアプリケーション作成チュートリアルを
終えておらずGWTの概念がある程度わかっているなら(慣れ親しんでいるなら)、
ここでStockWatcherプロジェクトをコードとしてインポートすることが出来ます。
- StockWatcher projectをダウンロードします。
- ファイルを解凍します。
- エクリプスへプロジェクトをインポートします。
ファイルメニューから、インポートメニューオプションを選択します。- [一般]-[既存プロジェクトをワークスペースへ]を選択します。
次へ(N)ボタンをクリックします。 - [ルート・ディレクトリーの選択(T)]でブラウズしStockWatcherのあるディレクトリ
(ファイルを解凍した場所から)を選択します。完了(F)ボタンをクリックします。
antを使用している場合は、StockWatcher/build.xmlの何処にGWTを解凍したかを指す
gwt.sdk プロパティを編集します。実際にこのチュートリアルを実行する為に、StockWatcherを実行しているサーバとは別に、あなたのマシン上でPHPスクリプトかPythonスクリプトを実行できるサーバのどちらかへのアクセスが必要です。
1. 要件と設計のレビュー
リモートサーバ上のデータにアクセスするために現在のStockWatcherの実装を修正するには、取り組むべき二つの課題があります:
- アクセス制限: SOP (Same Origin Policy セイムオリジンポリシー※)
- 非同期通信
アクセス制限: Same Origin ポリシー
Same Origin Policy (SOP)とはクライアントサイドJavaScriptコードが同じドメイン、プロトコル、ポートから発していない資源(リソース)と対話することを制限するブラウザのセキュリティ対策です。
この3種の値が同じ場合のみ、ブラウザは2ページが同じ発生元をもつ考えます。
例えば、http://abc.com:80上のWebページで実行されているStockWatcherアプリケーションは、異なるドメイン、http://xyz.comからロードされた株データと対話できません。 ポートが異なる場合(例えばhttp://abc.com:81)、同じドメインから株データであってもロードすることは出来ません。
この3種の値が同じ場合のみ、ブラウザは2ページが同じ発生元をもつ考えます。
例えば、http://abc.com:80上のWebページで実行されているStockWatcherアプリケーションは、異なるドメイン、http://xyz.comからロードされた株データと対話できません。 ポートが異なる場合(例えばhttp://abc.com:81)、同じドメインから株データであってもロードすることは出来ません。
SOPの背後にある考えは、セキュリティ上の理由から、ブラウザは任意のWebサイトの内容を信頼すべきではないということです。悪意あるWebページはデータを盗むまたはセキュリティを破るコードを注入することが出来ます。
従って異なるドメインまたはポートからのJSONフォーマット化された株データへのアクセスは、現在の実装では機能しません。webブラウザはJSONを取得するためのHTTPコールをブロックするでしょう。
SOPとGWT上でのその影響についてのより詳しい情報は What is the Same Origin Policy, and how does it affect GWT?を読んでください。
SOPの回避
SOPセキュリティを回避するための選択肢は二つあります:
- あなたのサーバ上でのプロキシ
- <script>タグ内へのJSONレスポンスのロード
あなたのサーバ上でのプロキシ
最初の選択肢はSOPのルールに従って行動し、ローカルサーバ上にプロキシを作成します。それからローカルサーバへHTTPコールを送り、ローカルサーバにリモートサーバからのデータを取りに行かせます。あなたのWebサーバ(ローカルサーバ)上で実行されるコードはSOPの制約を受けないのでこれは機能します。クライアントサイドコードのみです。
特にStockWatcherアプリケーションでは、リモートサーバからJSONエンコード化された株式相場をダウンロード(と多分キャッシュ)するためのサーバサイドコードを書くことによってこの方法を実装出来ます。それからローカルサーバからデータを取得するための望みどおりの任意のメカニズムを使用出来ます: GWT RPC または RequestBuilderを使用した直接のHTTPコール
このアプローチのマイナス面の一つは、サーバサイドコードの追加が必要な事です。 もう一つは、余分なHTTPコールがリモートコールの呼び出し時間を増加させ、webサーバの作業負担を追加するということです。
<script>タグ内へのJSONレスポンスのロード
もう一つの選択肢は<script>タグ内へ動的にJavaScriptをロードすることです。クライアントサイドのJavaScriptは、HTMLドキュメントオブジェクトモデル(DOM)内の任意の他の要素のように、<script>タグを操作出来ます。クライアントサイドコードはページへ新しいJavaScriptを自動的にダウンロードして実行するために<script>タグのsrc属性をセット出来ます。
この方法はSOPの制約を受けません。従って、リモートサーバからJavaScript(及びJSON)をロードするためにこの方法は効果的に使用できます。
これはリモートサーバからJSONフォーマット化された株データを取得するために使用する方法です。
非同期通信
<script>タグへのJavaScriptの動的ローディングはSOP問題を解決しますが、別の問題を持ち込みます。JavaScriptをロードするためにこのメソッドを使用した時、ブラウザは非同期にコードを取得しますが、その取得が完了した時の通知をしません。その代わりに新しいJavaScriptを単純に実行します。しかしながら、当然、JSONは実行可能なコードを含むことは出来ません。二つを一緒に置いてください。<script>タグを使用してプレーンJSONデータをロードすることが出来ないことがわかります。
JSON with Padding (JSONP)
コールバック問題を解決するためにそのコール(呼び出し)自身の入力引数としてコールバック関数の名前を指定することが出来ます。そうするとwebサーバはその関数へのコールでJSONレスポンスをラップします。 このテクニックは JSON with Padding (JSONP)と呼ばれます。 ブラウザが<Script>タグの新しい内容のダウンロードを終えた時、コールバック関数は実行されます。
callback125([{"symbol":"DDD","price":10.610339195026,"change":0.053085447454327}]);
Google Data APIs はこのテクニックをサポートします。
StockWatcherのための、クライアントサイドコード内の追加の要件はHTTPリクエストのコールバックとして使用するJavaScriptの関数の名前を含めることです。
実装計画
クロスサイトリクエストを取り巻くSOP問題を理解している今、この実装とローカルサーバからJSONデータを取得してくる実装とを比較します。あなたは既存の実装をいくらか修正しなければなりませんが、同様にいくつかのコンポーネントは再利用することが出来ます。作業のほとんどは新しいメソッド、getJSONを書くことです。このメソッドはリモートサーバをコール(呼び出し)します。
| タスク | 同一サイト用 インプリメンテーション(実装) | クロスサイトインプリメンテーション(実装) |
|---|---|---|
| コールの作成 | リクエストビルダーによるHTTP | 付加されたコールバック関数の名前を伴うJSONデータのURLであるsrc属性をもつ<script>タグを埋め込んでください? |
| サーバサイドコード | JSON文字列を返します | JSON文字列を伴うJavaScriptコールバック関数を返します |
| レスポンスの処理 | JSON文字列をJavaScriptオブジェクトへ変更する為にJavaScriptのeval()関数を使用 | 既にJavaScriptオブジェクト;StockData配列にキャストされる |
| データオブジェクト | オーバーレイタイプを作成: StockData | オーバーレイタイプを再利用 |
| エラー処理 | エラーメッセージを表示するためのレーベルウィジェットを作成 | レーベルウィジェットを再利用 |
2. データソースの作成
このチュートリアルではStockWatcherはSOPの制約に遭遇するので株データをセットアップするための二つの選択肢があります。
- PHPがインストールされたサーバへアクセス出来る場合、JSONフォーマット化された株データを生成するために以下のPHPスクリプトを使用出来ます。
- サーバを持っていないけれど、あなたのマシンにPythonがインストールされている場合、StockWatcherが実行されているポートと異なるポートから株データを提供するために以下のPythonスクリプトを使用出来ます。
実際に異なるサーバを使用する
webサーバにアクセス出来たら、JSONPを返す為の以下のPHPスクリプトを使用することが出来ます。
- テキストファイルを作成し
stockPrices.phpという名前にします。<?php header('Content-Type: text/javascript'); header('Cache-Control: no-cache'); header('Pragma: no-cache'); define("MAX_PRICE", 100.0); // $100.00 define("MAX_PRICE_CHANGE", 0.02); // +/- 2% $callback = trim($_GET['callback']); echo $callback; echo '(['; $q = trim($_GET['q']); if ($q) { $symbols = explode(' ', $q); for ($i=0; $i<count($symbols); $i++) { $price = lcg_value() * MAX_PRICE; $change = $price * MAX_PRICE_CHANGE * (lcg_value() * 2.0 - 1.0); echo '{'; echo "\"symbol\":\"$symbols[$i]\","; echo "\"price\":$price,"; echo "\"change\":$change"; echo '}'; if ($i < (count($symbols) - 1)) { echo ','; } } } echo ']);'; ?> - 別のサーバにPHPスクリプトをコピーします。
- ブラウザを開き、JSONデータ用のリクエストを作成します。
http://[www.myStockServerDomain.com]/stockPrices.php?q=ABC - JSON文字列が返されます。
[{"symbol":"ABC","price":81.284083,"change":-0.007986}]しかしながら、 次のセクションでわかるように、StockWatcherアプリケーションは自身のクライアントコードからこのリクエストをすることは出来ません。 - コールバック関数の名前を付加することによってJSONP用のリクエストをします。
http://[www.myStockServerDomain.com]/stockPrices.php?q=ABC&callback=callback125 - JSONはコールバック関数に埋めこまれて返されます。
callback125([{"symbol":"ABC","price":53.554212,"change":0.584011}]);
セカンドサーバのシミュレーション(Pythonによる模擬セカンドサーバ)
リモートサーバへ出来ない場合でも、あなたのローカルマシンにPythonがインストールされているのなら、リモートサーバをシミュレート出来ます。異なるポートへHTTPリクエストをした場合、あなたは まるで異なるドメインへアクセスしようとしているかのようにSOPの制約を受けます。
あなたのローカルマシンの異なるポートからデータを提供するために以下のスクリプトを使用します。 個々の株のシンボルのためにPythonスクリプトはJSONフォーマットのランダムな価格を生成し、値を変更します。BaseHTTPServer.HTTPServerコンストラクタ内でそれはポート 8000で実行されていることに注意してください。さらにスクリプトはコールバッククエリ文字列パラメータをサポートすることにも注意してください。
- Pythonスクリプトを作成し、
quoteServer.pyとして保存します。#!/usr/bin/env python2.4 # # Copyright 2007 Google Inc. All Rights Reserved. import BaseHTTPServer import SimpleHTTPServer import urllib import random MAX_PRICE = 100.0 MAX_PRICE_CHANGE = 0.02 class MyHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): def do_GET(self): form = {} if self.path.find('?') > -1: queryStr = self.path.split('?')[1] form = dict([queryParam.split('=') for queryParam in queryStr.split('&')]) body = '[' if 'q' in form: quotes = [] for symbol in urllib.unquote_plus(form['q']).split(' '): price = random.random() * MAX_PRICE change = price * MAX_PRICE_CHANGE * (random.random() * 2.0 - 1.0) quotes.append(('{"symbol":"%s","price":%f,"change":%f}' % (symbol, price, change))) body += ','.join(quotes) body += ']' if 'callback' in form: body = ('%s(%s);' % (form['callback'], body)) self.send_response(200) self.send_header('Content-Type', 'text/javascript') self.send_header('Content-Length', len(body)) self.send_header('Expires', '-1') self.send_header('Cache-Control', 'no-cache') self.send_header('Pragma', 'no-cache') self.end_headers() self.wfile.write(body) self.wfile.flush() self.connection.shutdown(1) bhs = BaseHTTPServer.HTTPServer(('', 8000), MyHandler) bhs.serve_forever() - メインのStockWatcherディレクトリへスクリプトを保存します。
- あなたのPATHにPythonインタプリタがあることを確認してください。(※PATHが通ってること?)
- スクリプトを実行します。コマンドシェルから
python quoteServer.pyと入力します。サーバは起動しますが、直接にはいかなる出力も目にすることはありません。(HTTPリクエストごとに記録されます。) - ブラウザを開き、JSONデータ用のリクエストをします。
http://localhost:8000/?q=ABC - JSON文字列が返されます。
[{"symbol":"ABC","price":81.284083,"change":-0.007986}]しかしながら、 次のセクションでわかるように、StockWatcherアプリケーションは自身のクライアントコードからこのリクエストをすることは出来ません。 - コールバック関数の名前を付加することによってJSONP用のリクエストをします。
http://localhost:8000/?q=ABC&callback=callback125 - JSONはコールバック関数に埋めこまれて返されます。
callback125([{"symbol":"ABC","price":53.554212,"change":0.584011}]);
3.リモートサーバからのデータリクエスト
サーバがJSON文字列またはJSONPとしていずれかの株データを返すことを確認した今、あなたはリクエスト及びJSOPを処理するようにStockWatcherを更新出来ます。RequestBuilderコードはgetjsonメソッドへの呼び出しに置き換えられます。最初のパラメータは各HTTPリクエストを一意的に識別するIDナンバーです。
URLの指定
クエリしている株コードとコールバック関数の両方を含むようにするためにクエリURLを更新します。個々のコールバック関数は一意的なIDナンバーを持ちます。
JSON_URLの更新
これはデータが異なるドメインまたは異なるポートから提供されるかに依存する実装を結果として生じることが唯一の違いです。
- StockWatcherクラスで、以下のように JSON_URL 定数を変更します:変更前:
private static final String JSON_URL = GWT.getModuleBaseURL() + "stockPrices?q=";
異なるポート(Pythonスクリプト)から株データが提供される場合、 JSON_URLは以下のよう変更します:
private static final String JSON_URL = "http://localhost:8000/?q=";
異なるドメイン(PHPスクリプト)から株データが提供される場合、ドメインとstockPrices.phpスクリプトへのフルパスを指定します:
private static final String JSON_URL = "http://www.myStockServerDomain.com/stockPrices.php?q=";
- コールバックIDの値を保持する変数を作成します。
private Label errorMsgLabel = new Label(); private int jsonRequestId = 0; - リモートサーバからのプレーンJSONデータの取得を試みます。開発モードでStockWatcherをデバッグします。株コードを入力します。
- StockWatcherはエラーメッセージを表示します: Couldn't retrieve JSON.SOPエラーを修正するために、次のセクションでコールバック関数を伴ったJSONを詰めます。
コールバックメソッドの作成
セイムサイト(クライアントとサーバがSOP制約を受けない)実装では、JSON URLが株コードに付加され、それからサーバへHTTP GET リクエストが送信されました。HTTPリクエストを構築する為にRequestBuilderを使用しました。
クロスサイト実装では、JSON URLはコールバックメソッドにラップされます。RequestBuilderのコードはJSNI関数のgetJSON(int, String, StockWatcher)によって置き換えられます。最初のパラメータは一意的に識別出来る各HTTPリクエストのIDナンバーです。
getJSONメソッドはサーバを呼び出すJavaScriptを作成します。
refreshWatchList メソッドの更新
- refreshWatchList メソッドを更新します。
/** * Generate random stock prices. */ private void refreshWatchList() { if (stocks.size() == 0) { return; } String url = JSON_URL; // Append watch list stock symbols to query URL. Iterator<String> iter = stocks.iterator(); while (iter.hasNext()) { url += iter.next(); if (iter.hasNext()) { url += "+"; } } // Append the name of the callback function to the JSON URL. url = URL.encode(url) + "&callback="; // Send request to server by replacing RequestBuilder code with a call to a JSNI method. getJson(jsonRequestId++, url, this); } - エクリプスはgetJsonにフラグをつけます。コンパイルエラーは無視します;あなたはすぐにこのメソッドを書きます。
- RequestBuilderのコードをまだ削除していない場合は削除してください。RequestBuilderのコードはgetJsonメソッドへの呼び出しへ置き換えます。従って、refreshWatchtList内の以下のコードは最早必要ではありません:
// Send request to server and catch any errors. RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, url); try { Request request = builder.sendRequest(null, new RequestCallback() { public void onError(Request request, Throwable exception) { displayError("Couldn't retrieve JSON"); } public void onResponseReceived(Request request, Response response) { if (200 == response.getStatusCode()) { updateTable(asArrayOfStockData(response.getText())); } else { displayError("Couldn't retrieve JSON (" + response.getStatusText() + ")"); } } }); } catch (RequestException e) { displayError("Couldn't retrieve JSON"); }
リモートサーバへの呼び出しの実行
以下の変更はこの実装で最も重要な変更です。リモートサーバへの呼び出しの実行に<script>タグのsrc属性を使用することによってSOPの制約を回避します。
このJSNI メソッド (getJSON)は動的にロードされる<script>タグを作成します。src属性はコールバック関数の名前が付加されたJSONデータのURLです。スクリプトが実行されると、 パッドされたJSONを取ってきます; JSONデータはコールバック関数の引数として渡されます。 コールバック関数が実行されると、JavaのhandleJsonResponseメソッドを呼び出し、JavaScriptオブジェクトとしてJSON
データを渡します。
データを渡します。
この実装は埋めこまれた手書きのJavaScriptを含むだけではなく、ブリッジメソッド(開発中、Javaソースコードへコールバックするためのテクニック)を使用します。(StockWatcherをコンパイルする時、クライアントサイドJavaコードは全てJavaScriptへコンパイルされることを思い出してください)
getJSONメソッドの実装
- StockWatcherクラスへgetJsonメソッドを追加します。
/** * Make call to remote server. */ public native static void getJson(int requestId, String url, StockWatcher handler) /*-{ var callback = "callback" + requestId; // [1] Create a script element. var script = document.createElement("script"); script.setAttribute("src", url+callback); script.setAttribute("type", "text/javascript"); // [2] Define the callback function on the window object. window[callback] = function(jsonObj) { // [3] handler.@com.google.gwt.sample.stockwatcher.client.StockWatcher::handleJsonResponse(Lcom/google/gwt/core/client/JavaScriptObject;)(jsonObj); window[callback + "done"] = true; } // [4] JSON download has 1-second timeout. setTimeout(function() { if (!window[callback + "done"]) { handler.@com.google.gwt.sample.stockwatcher.client.StockWatcher::handleJsonResponse(Lcom/google/gwt/core/client/JavaScriptObject;)(null); } // [5] Cleanup. Remove script and callback elements. document.body.removeChild(script); delete window[callback]; delete window[callback + "done"]; }, 1000); // [6] Attach the script element to the document body. document.body.appendChild(script); }-*/;
実装の注意
- [1] このスクリプトは<script>要素をセットアップすることによって開始します。src属性は、コールバック関数にラップされたJSONデータを取得するURLを指します。
- [2] コールバック関数はブラウザのwindowオブジェクト上で定義されます。引数としてサーバによって返されたJSONデータであるJavaScriptオブジェクトを受け取ります。
- [3] コールバック関数はJavaメソッド handleJsonResponseへJavaScriptオブジェクトとしてJSONデータを渡します。
- [4] 無応答のサーバやネットワークの問題をチェックするためにタイムアウト関数を定義します; JSONコールバックが呼ばれたかどうかわかるフラグをチェックします。
- [5]タイムアウト関数が完了する前に、windowから新しい<script>要素とコールバック関数を除去します。
- [6] 最後に動的にロードされた<script>要素をHTMLドキュメントボディへ添付するためにappendChild()を呼び出します。これはwebブラウザにsrc属性によって参照されるJavaScriptをダウンロードさせます。
未完了の複数のリクエストの処理
この実装は未完了の複数のリクエストがある場合、コールバック関数の名前をシーケンシャルに生成します。特にhandleJsonResponse(JavaScriptObject)メソッドを呼び出すために使用される文法に注目してください:
handler.@com.google.gwt.sample.stockwatcher.client.StockWatcher::handleJsonResponse(Lcom/google/gwt/core/client/JavaScriptObject;)(jsonObj);
JSONオブジェクトがダウンロードされたら、JSNIメソッド内のコールバックがJavaメソッド handleJsonResponseへデリゲート(委譲)していることがわかります。
ブリッジメソッドについて
JavaScriptからJavaメソッドを呼び出すことはJNIでC言語のコードからJavaメソッドを呼び出すに多少似ています。 特に、JSNIは オーバーロードされたメソッドと区別するためにJNIマングルドメソッドシグニチャアプローチを借りています。JavaメソッドへのJavaScriptの呼び出しは以下の形式になります:
[instance-expr.]@class-name::method-name(param-signature)(arguments)
※mangle … 〈…を〉めった切りにする/(間違いや不適切な引用などで)〈文章などを〉わからなくする; ぶちこわす/〈洗濯物などを〉水絞り機にかける.
name mangling … 名前修飾 プログラム内の各種実体に一意の名前を与えることで、様々な問題を解決する(name mangling)
name mangling と extern "C" や What's in Name Mangling?を読むと、C++でシグネチャを一意的なシンボル名に変換することらしい。
5.4 ファイル名の短縮 (Name Mangling) と大文字小文字 ではロングファイルネームを可能にするための手法として説明されている。
weblioの説明の通り、一意的な名前を用いて何らかの問題解決をはかることらしい。
name mangling … 名前修飾 プログラム内の各種実体に一意の名前を与えることで、様々な問題を解決する(name mangling)
name mangling と extern "C" や What's in Name Mangling?を読むと、C++でシグネチャを一意的なシンボル名に変換することらしい。
5.4 ファイル名の短縮 (Name Mangling) と大文字小文字 ではロングファイルネームを可能にするための手法として説明されている。
weblioの説明の通り、一意的な名前を用いて何らかの問題解決をはかることらしい。
| コンポーネント | 説明 | 例 |
|---|---|---|
| [instance-expr.] | インスタンスメソッドを呼び出す時に存在しなければならず、スタティックメソッドを呼び出す時は存在してはいけません。 | handler. (StockWatcher object instance) |
| @class-name | StockWatcherクラスの完全修飾名 | @com.google.gwt.sample.stockwatcher.client.StockWatcher |
| ::method-name | 呼び出すメソッドの名前 | ::handleJsonResponse |
| (param-signature) | JNI構文で定義された handleJsonResponse メソッドシグニチャ | (Lcom/google/gwt/core/client/JavaScriptObject;) |
| (arguments) | JSONデータを含んだjsonObj | (jsonObj) |
JavaScript実装のJSNIメソッドからのJavaオブジェクトの操作についての更なる情報は開発者ガイドのAccessing Java Methods and Fields from JavaScriptを見てください。
4. レスポンスの処理
この時点であなたの作業のほとんどは済みます。唯一の違いは返ってくる値が既にJavaScriptオブジェクトであり、JSON文字列ではないということです。従って、最早asArrayOfStockDataメソッドで、JSON文字列を変換するためにJavaScript eval()関数を使用する必要はありません。
handleJsonResponse メソッドを実装する
サーバからレスポンス(応答)を受け取ったら、価格と変動値フィールドへ反映させるためにupdateTableメソッドを呼び出します。あなたは依然としてセイムサイト実装で書いたオーバーレイタイプ(StockData)とJsArray(asArrayOfStockData)を使用しているでしょう。
サーバからレスポンスが来ない場合、メッセージを表示します。セイムサイト実装で作成した、同じdisplayErrorメソッドとレーベルウィジェットを使用出来ます。
- StockWatcherクラスへhandleJsonResponseメソッドを追加します。
/** * Handle the response to the request for stock data from a remote server. */ public void handleJsonResponse(JavaScriptObject jso) { if (jso == null) { displayError("Couldn't retrieve JSON"); return; } updateTable(asArrayOfStockData (jso)); }エクリプスはJavaScriptObjectにフラグをつけます。 - import宣言を追加します。
import com.google.gwt.core.client.JavaScriptObject;
エクリプスはasArrayOfStockDataにフラグをつけます。asArrayOfStockData はJavaScriptObjectではなくStringを期待します。
arrayOfStockDataメソッドを修正する
この実装ではレスポンスはStringではなくJavaScriptオブジェクトです。あなたの次のステップはasArrayOfStockDataメソッドを修正することです。JSON文字列をJavaScript配列に変換するよりも、JSNIメソッドから返されたJavaScriptオブジェクトをStockDataの配列としてキャストする必要があります。
- asArrayOfStockData メソッドを以下のように修正します:変更前:
/** * Convert the string of JSON into JavaScript object. */ private final native JsArray<StockData> asArrayOfStockData(String json) /*-{ return eval(json); }-*/;変更後:
/** * Cast JavaScriptObject as JsArray of StockData. */ private final native JsArray<StockData> asArrayOfStockData(JavaScriptObject jso) /*-{ return jso; }-*/;
5. テスト
あなたが異なるドメインまたは異なるポートのどちらかからJSONフォーマット化された株データを提供することを選択したかに関わらず、新しいStockWatcher実装はいかなるSOPアクセス制約をも回避して株データを取得出来るべきです。
開発モードでテストする
異なるポートから株データを提供する
- Pythonサーバが実行されていることを確認します。もし実行されていなかったら、コマンドラインで
python quoteServer.pyを入力します。 - 開発モードで実行されているブラウザ内で、StockWatcherをリフレッシュします。
- 株コードを追加します。StockWatcherは価格と変動データを表示します。現在、情報は異なるポートから来ます。
- Pythonサーバをシャットダウンします。StockWatcherはエラーを表示します: Couldn't retrieve JSON
- Pythonサーバを再起動します。StockWatcherはエラーを消去し、価格と変動の更新を表示し続けます。
異なるドメインから株データを提供する
- 開発モードで実行されているブラウザ内で、StockWatcherをリフレッシュします。
- StockWatcherは価格と変動データを表示します。現在、情報はリモートサーバから来ます。株コードを追加します。
- StockWatcher.javaで、JSON_URLの値を不正な値に変更します。
- 開発モードで実行されているブラウザ内で、StockWatcherをリフレッシュします。株コードを追加します。StockWatcherはエラーを表示します: Couldn't retrieve JSON
- StockWatcher.javaで、JSON_URLの値を正しい値に直します。
開発モードで実行されているブラウザ内で、StockWatcherをリフレッシュします。株コードを追加します。StockWatcherはエラーを消去し、価格と変動の更新を表示し続けます。
次は何?
セキュリティとクロスサイトリクエスト
あなた自身のマッシュアップを実装する前に、クロスサイトでJSONデータをダウンロードすることは強力だが、セキュリティリスクもまた有るということを思い出して欲しい。あなたが対話するサーバはあなたのアプリケーション内で任意のJavaScriptコードを実行することが出来るので、サーバが完全に信頼出来るということを確かめてください。Security for GWT Applicationsを読むために時間を割いてください。GWTアプリケーションに対する潜在的脅威とそれらとどう戦うかを説明してます。