TableauWDCで CORSを回避する方法(超意訳)


WDCをやっていると必ずと行っていいほど起きる問題が、CORS(cross-origin resource sharing)のようです。
それの対策について、まとまっていたので、つたないですが超意訳(?)してみます。


WDCではJavaScriptのコードで実装されていて、実際にこのコードが配置されるサーバ(ドメイン)とは別のWebサーバにAPIリクエストをして、データを取得する場合がほとんどと思います。
これはすなわちCORS(cross-origin resource sharing)と呼ばれるものですが、セキュリティ上、ブラウザはこのCORSを制限しています(特にChromeとか)
※WDCでは、Tableauの内部ブラウザを使っているので、CORSを回避するChromeのプラグインは利用できない・・・

CORS制限のために、ブラウザのコンソールに下記のようなエラーが出ると思います

XMLHttpRequest cannot load URL. No 'Access-Control-Allow-Origin' header is present on the requested resource.

このエラーを回避するのにいくつかの方法があるのでそちらを説明します

  • ローカルにプロキシを立てる(Make requests through a proxy server)
  • 利用するAPIサーバにCORSを許可するようにお願いする(Request CORS support from API Server)
  • JSONPをつかってAPIへリクエストを送る(Make requests with JSONP)

※注釈 Debugging in the Simulator and Tableau) でデバッグの方法が書いてますが、その中にもCORSのエラーについては書いてあるっぽい

ローカルにプロキシを立てる(Make requests through a proxy server)

ブラウザのCORS制限を回避するために、プロクシサーバを立てて、それを経由して、リクエストを送る方法があります。プロキシーは単純にデータを取得するだけの動きをします(データ加工などは一切しない)
このプロキシはブラウザで動くわけではないので、ブラウザのCORS制限には引っかからないのです

データの取得先のドメインが、WDCサーバと同じならCORS問題は起きませんが、別ドメインからのデータ取得だとCORS問題がおきます。
もしプロキシサーバのドメインが別なら、そのプロキシサーバで Access-Control-Allow-Origin をヘッダにセットすれば動きます。

WDCSDK には、そのまま使えるプロキシサーバが設定されています。

webdataconnectorのリポジトリの第一階層で、下記のコマンドをTerminalなどで打ちます。

npm start

これで、テストサーバとテストプロキシサーバが起動します

プロキシは下記のURLになる

http://localhost:8889

例えば earthquakeUSGS のコネクタの場合, $.getJSON の記載は

$.getJSON("http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/4.5_week.geojson"

となってますが、こちらを

$.getJSON("http://localhost:8889/earthquake.usgs.gov/earthquakes/feed/v1.0/summary/4.5_week.geojson"

と変更することで、プロクシ経由でCORS制限を回避してアクセスできます。
重要: httpのみしか対応してない。
※httpsのAPIにアクセスする場合は、 Tableau WDCの「Allow-Control-Allow-Origin: *」問題を回避してみるを参考にしてみてください。

フリーのプロキシ にはこんなのがある(らしい)。こちらを使うのも手

利用するAPIサーバにCORSを許可するようにお願いする

Access-Control-Allow-Origin をヘッダにつけてもらうようにAPI提供先にお願いする
(それで解決できるならこれが一番だとは思うが!)

JSONPを使う

CORS対策をしていないサーバでも、JSON with padding もしくは JSONP でデータが取得できる場合が多くあります。直接JavaScriptを使うとブロックされてていても、JSONPを使えば可能(?)
その代わりに、JavaScriptを使用して <script> 要素を動的に作成または更新し、src属性をデータを持つサイトを指すURLに設定します。URLには、コネクタページにあるコールバック関数の名前も含まれています。サイトがJSONPをサポートしている場合、レスポンスにはJSON形式のデータが含まれており、コールバック関数の呼び出しで「パディング」されます。

Webデータ・コネクターでjQueryを使用する場合は、ajax関数を使用してJSONP呼び出しを行うことができます。詳細は、jQueryサイトのjQuery.ajax()を参照してください。

jQueryを使用していない場合は、JavaScriptコードでJSONPを実装できます。たとえば、コネクタに次のJavaScriptコードが含まれていると、<script>要素が動的に作成され、src属性が架空のURLに設定されます。

var scriptTag = document.createElement('script');
scriptTag.src = "http://myserver/temperatures?city=Seattle&year=2014&callback=getTemperatures";
document.getElementsByTagName('head')[0].appendChild(scriptTag);

コネクタには、サイトが返す「埋め込まれた」JSONによって呼び出される次の関数も含まれている必要があります。 JSONデータはjsonpDataパラメータとして関数に渡され、関数内のコードはJSONブロックの場合と同じように値を抽出できます。


var getTemperatures = function(jsonpData) {
        alert(jsonpData.someProperty);
        // More code here to extract the JSON data
    }

参考