GroovyでJSONを受け答えするAPIサーバを作る(Embedded Jetty)

以前書いた記事はGET限定だったので、POSTも作ってみた。
<a href="http://qiita.com/it__ssei/items/d40aed128dc8f4269aa5">GETのみのAPIサーバ作ってみた記事</a>

# TL;DR
* Groovyでjax-rs使う
* 設定ファイルは書かない
* JSONをPOSTで受け取り、JSONを返す

# 実装
## サーバ

* Getの時と同じ。
* リソースクラスのみ別途作成したので変更した

```groovy:server.groovy
@Grab('org.eclipse.jetty.aggregate:jetty-all:9.2.6.v20141205')
@Grab('com.sun.jersey:jersey-server:1.18.3')
@Grab('com.sun.jersey:jersey-servlet:1.18.3')
@Grab('com.sun.jersey:jersey-core:1.18.3')
@Grab('com.sun.jersey:jersey-json:1.18.3')

import com.sun.jersey.spi.container.servlet.ServletContainer

import org.eclipse.jetty.server.handler.ContextHandler
import org.eclipse.jetty.server.handler.HandlerList
import org.eclipse.jetty.server.handler.ResourceHandler
import org.eclipse.jetty.servlet.ServletContextHandler
import org.eclipse.jetty.servlet.ServletHolder
import org.eclipse.jetty.server.Server


/*----- jersey -----*/
//JAX-RSのresource、providerに含めるクラスを指定
def ResourceClass = [
    PostResource.class,
]
def servletContext = new ServletContextHandler(ServletContextHandler.SESSIONS);
def servletHolder = new ServletHolder(ServletContainer.class);

//サーブレットとして動作させるクラス名を指定（"HogeClass;FugaClass;…"の形）
servletHolder.setInitParameters([
    "com.sun.jersey.config.property.resourceConfigClass": "com.sun.jersey.api.core.ClassNamesResourceConfig",
    "com.sun.jersey.config.property.classnames": ResourceClass.collect{it.getName()}.join(";"),
    "com.sun.jersey.api.json.POJOMappingFeature": "true"
])
servletContext.setContextPath("/api")        //URL設定
servletContext.addServlet(servletHolder, "/*")

/*----- サーバ起動 -----*/
def server = new Server(8880);               //ポート設定
//コンテキスト定義をサーバにハンドル
def handlerList = new HandlerList()
handlerList.setHandlers(servletContext)
server.setHandler(handlerList)

println "APIサーバーを起動します"
server.start()                               //サーバ起動

/*----- 終了時 -----*/
addShutdownHook {
  println "APIサーバーを停止します"
  server.stop()
}
```

## リソースクラス
受け取ったデータをログに出すだけのメソッドと
受け取ったデータをもとにJSON返すメソッドを実装する。

```groovy:PostResource.groovy
import javax.ws.rs.Consumes
import javax.ws.rs.Path
import javax.ws.rs.POST
import javax.ws.rs.Produces
import javax.ws.rs.core.MediaType

@Path("/gochi")
class PostResource {

    @POST
    @Path("/")
    @Consumes(MediaType.APPLICATION_JSON)
    public void readPostData(String jsonData) {
        println jsonData
    }

    @POST
    @Path("/usa")
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public PostResultDto post(PostRequestDto dto) {
        println dto
        println dto.name1
        println dto.name2
        println dto.name3
        println dto.name4
        return new PostResultDto("test", "test", "test", "test")
    }
}
```

データクラス

```groovy:PostResultDto.groovy
import javax.xml.bind.annotation.XmlRootElement

@XmlRootElement
class PostResultDto {
    String name1
    String name2
    String name3
    String name4

    //引数なしでもnewできるようにする
    PostResultDto() {
    }

    PostResultDto(name1, name2, name3, name4) {
        this.name1 = name1
        this.name2 = name2
        this.name3 = name3
        this.name4 = name4
    }
}
```


# 確認
APIサーバを起動した後、curlでPOSTする。

## APIサーバを起動する

```shell-session
groovy server.groovy
```

## 受け取ったデータをログに出すだけのAPIを叩く

```bash
curl localhost:8880/api/gochi -v -X POST -H "Accept: application/json" -H "Content-type: application/json" -d '{"api":"test"}'
```

詳細表示のためいろいろ出るが、ちゃんとアクセスできる

```shell-session:結果
 application/json" -d '{"api":"test"}'
* About to connect() to localhost port 8880 (#0)
*   Trying ::1...
* Connected to localhost (::1) port 8880 (#0)
> POST /api/gochi HTTP/1.1
> User-Agent: curl/7.29.0
> Host: localhost:8880
> Accept: application/json
> Content-type: application/json
> Content-Length: 14
>
* upload completely sent off: 14 out of 14 bytes
< HTTP/1.1 204 No Content
< Date: Wed, 25 May 2016 07:51:44 GMT
< Server: Jetty(9.2.6.v20141205)
<
* Connection #0 to host localhost left intact
```

## 受け取ったデータをもとにJSON返すAPIを叩く

```bash
curl localhost:8880/api/gochi/usa -v -X POST -H "Accept: application/json" -H "Content-type: application/json" -d '{"name1":"test1", "name2":"test2", "name3":"test3", "name4":"test4"}'
```

(ログの末尾にて)jsonで返ってくる。

```shell-session:結果
* About to connect() to localhost port 8880 (#0)
*   Trying ::1...
* Connected to localhost (::1) port 8880 (#0)
> POST /api/gochi/usa HTTP/1.1
> User-Agent: curl/7.29.0
> Host: localhost:8880
> Accept: application/json
> Content-type: application/json
> Content-Length: 68
>
* upload completely sent off: 68 out of 68 bytes
< HTTP/1.1 200 OK
< Date: Wed, 25 May 2016 07:54:24 GMT
< Content-Type: application/json
< Transfer-Encoding: chunked
< Server: Jetty(9.2.6.v20141205)
<
* Connection #0 to host localhost left intact
{"name1":"test","name2":"test","name3":"test","name4":"test"}
```


curl叩き方は<a href="http://takuya71.hatenablog.com/entry/2012/11/10/143415">こちら</a>を参考にした。

# 謝辞
「<a href="http://qiita.com/kimromi/items/1c797b1f53fbc1412c8d">Groovy+Jettyで簡単APIサーバ</a>」という@kimromiさんの記事中のAPIサーバを下敷きに、実現しかったPOST機能実装まで到達しました。この場をお借りして御礼申し上げます。良記事ありがとうございます。