[*その他*] アプリからの外部データの取得（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パラメーターとして設定しとけばよい。
受け取ったデータを

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

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

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

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

```java: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: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: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>");
    }
}
```

# 実行結果
ボタンを押下するとボタンの下に取得したデータが表示される。
![スクリーンショット 2015-12-08 17.08.44.png](https://qiita-image-store.s3.amazonaws.com/0/48912/da86a92c-9885-b4b8-5926-455a7da3eedf.png)

# まとめ
[気象情報API](http://developer.yahoo.co.jp/webapi/map/openlocalplatform/v1/weather.html)のようなJSONPを提供しているところがあるので、そういうところのAPIを利用したり、サーバー側でいい感じに返すようにしたらいいと思いました。