[Groovy]RatpackでシンプルなWEBアプリケーションを開発する -JSONとJSONPを扱う-

これはG* Advent Calendar 2015の5日目の記事です。

# おさらい
さて、[昨日の投稿](http://qiita.com/saba1024/items/efcc75a6685240188b6b)で、Ratpackでの基本的なHTTPルーティングの方法がわかりました。
コレで簡単なAPIサーバを構築する準備ができました！
今回は、さらにクライアントとRatpack間でJSONでデータをやりとりする方法を見て行きましょう。

# コード
今回のコードの全体は以下のようになります。

```groovy:ratpack.groovy
import static ratpack.groovy.Groovy.ratpack
import static ratpack.jackson.Jackson.json
import static ratpack.jackson.Jackson.jsonNode
import groovy.json.JsonSlurper
import groovy.json.JsonBuilder

ratpack {
    handlers {
        path("getjson"){
            response.headers.set 'Content-Type', 'application/json'
            // 注意！jQueryなどでJSONPで値が渡ってきた時は、そもそもそれってJSONではないのでここでは例外が発生する
            //ratpack.parse.NoSuchParserException: Could not find parser able to produce object of type 'com.fasterxml.jackson.databind.JsonNode' from request of content type 'null' and parse options 'JsonParseOpts{objectMapper=null}'. The following parsers were tried:
            render it.parse(jsonNode()).map{
                // ... promiseなんとか。。。
                def name = it.get("name").asText()
                def title = it.get("title").asText()

                json( ["postedName": name, "postedTitle":title]  )
            }
        }

        // JSONPの場合はGETで投げられる。（jQueryでPOSTとか指定していても無意味）
        get("getjsonp"){
            //http://tsujimotter.info/2013/01/03/jsonp/

            String requestedCollbackMethodName = request.queryParams.callbackName
            response.headers.set 'Content-Type', 'application/javascript'
            JsonBuilder jsonResponse = new JsonBuilder()
            jsonResponse(
                    'postedName' : request.queryParams.name,
                    'postedTitle' : request.queryParams.title
            )
            render """${requestedCollbackMethodName}(${jsonResponse})"""
        }
    }
}
```

# JSONを受け取り、JSONを返す

まずJSON(` path("getjson")`の方)です。
該当コードを抜き出すと以下の部分になります。

```groovy
path("getjson"){
    response.headers.set 'Content-Type', 'application/json'
    // 注意！jQueryなどでJSONPで値が渡ってきた時は、そもそもそれってJSONではないのでここでは例外が発生する
    //ratpack.parse.NoSuchParserException: Could not find parser able to produce object of type 'com.fasterxml.jackson.databind.JsonNode' from request of content type 'null' and parse options 'JsonParseOpts{objectMapper=null}'. The following parsers were tried:
    render it.parse(jsonNode()).map{
        // ... promiseなんとか。。。
        def name = it.get("name").asText()
        def title = it.get("title").asText()

        json( ["postedName": name, "postedTitle": title]  )
    }
}

```

コメントをちらっと見ればなんとなく解ると思いますが、前回POSTされたデータを取得する方法のところでも出てきたPromiseがここにも。。。
PromiseはRatpackにおいてとても重要な概念のようです。
基本的にはまぁコードを見てください。。。という感じなのですが、この注目する点は、JSONを扱うのに`Jackson`というクラスを利用している点です。
Ratpackが提供してくれているので、単純にimport文を書くだけで利用できます。
Jacksonの機能を利用して、`jsonNode()`をパースして、結果プロミスが返されるので、mapメソッドの中で実際にJSONでクライアントから投げられたデータを取得することが出来ます。
そして、クライアントにJSONデータを返す際にも、`Jackson.json()`というメソッドにMapを渡してあげれば自動的にJSONフォーマットに変換してくれてとても簡単にJSONを生成できます。`import static`しているので、`Jackson.json()`と書かずに`json()`と書くだけで利用できています。

## Groovyからテストしてみる

前回同様、`curlコマンド`でテストも当然できるのですが、ついでなのでGroovyからAPIを叩く為の方法も同時に試してみます。
Groovyには、[HTTPBuilder](https://github.com/jgritman/httpbuilder)というイケイケな通信用ライブラリがあります。
コレを使えばGroovyからお手軽にPOSTやGETで通信できます。

以下のソースを見ていただければなんとなく解ると思いますが、JSONを送って、レスポンスもJSONとして取得するサンプルです。

```groovy:json-client.groovy
@Grab('org.codehaus.groovy.modules.http-builder:http-builder:0.7.1')
import groovyx.net.http.HTTPBuilder
import static groovyx.net.http.ContentType.*
import static groovyx.net.http.Method.*

def http = new HTTPBuilder( 'http://localhost:5050/getjson' )
http.request(POST, JSON) {
    uri.path = '/getjson'
    body = [name: 'こうじ', title: 'hogehoge']
    response.success = { resp, json ->
        println "Success! ${resp.status}"
        println "$json"
    }

    response.failure = { resp ->
        println "Request failed with status ${resp.status}"
    }
}
```

Groovyの必殺技`Grab`を使っているのでただこのgroovyスクリプトを保存して、groovyコマンドで実行するだけです！
RatpackがちゃんとJSONを扱えて、Groovyからもこんなにお手軽にJSONが送信できることが解ると思います。

# jQueryからJSONPでデータを送信する
さて、JSONを送信といえばAjaxでjQueryから送信するというのはもう非常によくあるパターンですね。
基本的に上記のGroovyバージョンでJSONが正しく扱えることは分かったので、ここではJSONPを試してみたいと思います。
該当部分を抜き出すと以下になります。

```groovy
// JSONPの場合はGETで投げられる。（jQueryでPOSTとか指定していても無意味） 
get("getjsonp"){
    //http://tsujimotter.info/2013/01/03/jsonp/

    String requestedCollbackMethodName = request.queryParams.callbackName
    response.headers.set 'Content-Type', 'application/javascript'
    JsonBuilder jsonResponse = new JsonBuilder()
    jsonResponse(
            'postedName' : request.queryParams.name,
            'postedTitle' : request.queryParams.title
    )
    render """${requestedCollbackMethodName}(${jsonResponse})"""
}

```

大雑把に言うとJSONPは、JavaScriptが動いているドメインとは別のドメインに対して、**擬似的に**AjaxでJSONを送受信する方法です。
あくまで擬似的です。なので、実際の所クエリストリングとしてRatpackにJSONデータが送信されてきますので、Ratpack側では当然クエリストリングとして処理をして、JavaScriptから指定された**コールバック関数名**で囲んで、JavaScriptコードとして返してあげます。

テスト用のHTMLを以下に用意しました。
デスクトップとか適当なディレクトリに保存して、ブラウザで開いてみてください。
`Get JSON`とうボタンをクリックすると、結果をconsole.logで出力しています。（ブラウザのJavaScriptコンソールなどで結果を確認してください）

```html:jsonp-client.html
<!DOCTYPE html>
<html>
<head>
<meta name="robots" content="noindex,nofollow" />
<meta charset="UTF-8">
<title>This is Template</title>
<script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.11.1.min.js"></script>
<script type="text/javascript">
$(document).ready(function(){
        // Initial Code
    $('#button').click(function(){
        var data = {
            'name' : 'こうじ',
            'title' : 'programmer',
            'callbackName': 'callbackMethod'
        }

        $.ajax({
          type: "get",
          url: 'http://localhost:5050/getjsonp',
          //data: JSON.stringify(data),
          data: data,   // JSONPはJSONにあらず！stringgifyすると、このJSONの値自体がサーバ側でキーになってしまうので、生で送る。
          contentType: 'application/json',
          dataType: 'jsonp',
          jsonpCallback: data.callbackName,
          success: function(json){
            console.log(json);
          },
          error:function(a, b, c){
            console.log(a);
            console.log(b);
            console.log(c);
          }
        });
    });
});
</script>
</head>
<body>
<p>Hello World!</p>
<button id='button'>Get JSON</button>
</body>
</html>
```

ちゃんと動くことが確認できると思います。

「送信されてくるJSONPが入れ子の場合どうなんの？」と思われた方は鋭い。
例えばJavaScriptからRatpackに送信されるJSONが以下のような場合、

```js
var data = {
    'name' : 'こうじ',
    'title' : 'programmer',
    'callbackName': 'callbackMethod',
    'records': {
        'a':123,
        'b':456
    }
}
```

Ratpack側では以下のように取得できます。

```groovy
request.queryParams.get("records[b]")
```
添字自体も含めてパラメタ名になるんですね。


# まとめ
いかがだったでしょうか。
これでRatpackを使って、JSONでデータを送受信するような簡単なAPIサーバが構築できるようになりました。
Ratpackの特徴であるノンブロッキングな部分や、プロミスなど、まだまだ興味はつきませんが、私の理解は今の所このあたり止まりですので、ここで一旦終了となります。

それでは、2016年も素敵なG*ライフを送りましょう！

# 参考
[Ratpack](https://ratpack.io/)
[Ratpackについて(前編)](http://grimrose.bitbucket.org/blog/html/2014/12/06/g_advent_calendar_2014_ratpack_01.html)
[Ratpackについて(後編)](http://grimrose.bitbucket.org/blog/html/2014/12/07/g_advent_calendar_2014_ratpack_02.html)
[Ratpackについて(延長戦)](http://grimrose.bitbucket.org/blog/html/2014/12/31/g_advent_calendar_2014_ratpack_overtime.html)
[HTTPBuilder](https://github.com/jgritman/httpbuilder)
[HTTPBUilder Wiki](https://github.com/jgritman/httpbuilder/wiki)