[*その他*] アプリからの外部データの取得(JSONP編)


はじめに

アプリから、外部のサーバーのデータを取りたい場合のJSONP編。

JSONPとは

通常、異なるドメインに対してAjax通信をしようとすると怒られる。
これをクロスドメイン制約という。

XMLHttpRequest cannot load http://exampleexample.com/. 
No 'Access-Control-Allow-Origin' header is present on the requested resource.
Origin 'https://example.com/' is therefore not allowed access.

それでも外部のサーバーのデータを取るのは不可欠なので、

http://example.com/jsonp_project/JsonpServlet?callback=displayCustomer

上記のようなURLでアクセスした時に、
callbackパラメーターの値をサーバー側で受け取り、

displayCustomer([{"age":16,"name":"テスト太郎"}])

のようなJavaScriptの関数を呼び出す形式でサーバー側からJSONデータ返ってくるようにしておく。
displayCustomerがクライアント側で呼び出したいJavaScriptの関数名となっている。
この関数名は任意のものにして、callbackパラメーターとして設定しとけばよい。
受け取ったデータを

<script>
  displayCustomer([{"age":16,"name":"テスト太郎"}])
</script>

な感じで画面側に追加して、クライアント側で定義してあるJavaScriptの関数(この場合displayCustomer)を実行することで、外部のデータを取得した上での処理を実行するというのがJSONP。

scriptタグが外部のソースを読み込みこめることを利用している。

手順

サーバー側でJSON形式にてデータを返すようにする。
今回はサーブレットで適当に作ったデータをJSONで返すように実装。
実際はデータベースからデータを取得して

servlet
@WebServlet(name = "JsonpServlet", urlPatterns = {"/JsonpServlet"})
public class JsonpServlet extends HttpServlet {

    protected void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");

        // URLからcallbackパラメーターに設定されている値を取得
        String callback = request.getParameter("callback");

        Customer[] customers = {
            new Customer("テスト太郎", 16)
        };

        // JSONにエンコード
        String jsonText = JSON.encode(customers);

        String responseData = "";
        if(callback == null || "".equals(callback)){
            responseData = jsonText;
        }else{
            // ここで関数名(JSON)の形式でレスポンスを返すように設定
            responseData = callback + "(" + jsonText + ")";
        }

        response.setCharacterEncoding("UTF-8");
        PrintWriter out = response.getWriter();
        out.print(responseData);
    }
}

画面はボタンがあって、それを押したらJSの関数を実行するのみ。

html
<ons-page>
    <ons-toolbar>
        <div class="center">AjaxJSON</div>
    </ons-toolbar>

    <div style="text-align: center">
        <br>
        <ons-button onclick="addScript()">
            JSONP
        </ons-button>

        <div id="ajax"></div>
    </div>
</ons-page>

callbackパラメーターにクライアント側で呼びたいJS関数の名前を指定。

js
// ボタンが押された時にデータを受け取ってscriptタグとして画面に追加
function addScript(){
    var script = document.createElement('script');
    script.src = 'http://example.com/jsonp_project/JsonpServlet?callback=displayCustomer';
    document.body.appendChild(script);
}

// 上記で「関数名(JSON)」の形式でスクリプトが追加され、これが呼ばれる
function displayCustomer(data){
    for(i = 0; i < data.length; i++){
        $("#ajax").append("<div>" + data[i].name + "</div>");
    }
}

実行結果

ボタンを押下するとボタンの下に取得したデータが表示される。

まとめ

気象情報APIのようなJSONPを提供しているところがあるので、そういうところのAPIを利用したり、サーバー側でいい感じに返すようにしたらいいと思いました。