GrailsでシンプルなRESTサーバを素早く立ち上げる

はじめに

これはWeb API Advent Calendar 2014、10日目のエントリです。

Grailsでは某RESTful DAO的なものがサクッと実装できますよ、というお話です。 だから何だ、とか、○○のFWでもできるぞこの野郎、といった苦情はご遠慮ください。

実装する

とりあえず、Grailsプロジェクトを作成して、ドメインクラス(Entityクラス)を作成します。

$ grails create-app rest-sample
$ cd rest-sample
$ grails create-domain-class Book
$ vi grails-app/domain/rest/sample/Book.groovy

Bookドメインクラスの中身を以下のように修正しましょう。 適当なtitleプロパティを追加して(制約はあってもなくても関係ない)、Resourceアノテーションを追加してるだけです。このResourceアノテーションがいわゆる魔法詠唱ポイントです。

package rest.sample

import grails.rest.Resource

// これをつけるだけで、まったくコントローラを実装しなくても指定したURIでREST APIが有効になる
@Resource(uri='/books')
class Book {
    // とりあえずタイトルだけ
    String title

    static constraints = {
        // 気の済むように制約を書く
        title maxSize: 1000, blank: false, unique: true
    }
}

とりあえず実装はこれだけでOK。 Grailsが手元にある状態からであればカップヌードルの待ち時間ぐらいでできますね。

アクセスしてみる

早速組み込みTomcatでサーバを起動して...

$ grails run-app
...
| Server running. Browse to http://localhost:8080/rest-sample

cURLでアクセスしてみましょう。

$ curl http://127.0.0.1:8080/rest-sample/books
<?xml version="1.0" encoding="UTF-8"?><list />

GETでリストを取得してみると、初期状態なので空ですね*1

本を2件登録してみます。ちまたで話題のアノ本ですね。

$ curl http://127.0.0.1:8080/rest-sample/books -X POST -d "title=プログラミングGroovy"
$ curl http://127.0.0.1:8080/rest-sample/books -X POST -d "title=Gradle徹底入門"

もう一度GETでリストを確認すると...

$ curl http://127.0.0.1:8080/rest-sample/books
<?xml version="1.0" encoding="UTF-8"?><list><book id="1"><title>プログラミングGroovy</title></book><book id="2"><title>Gradle徹底入門</title></book></list>

うまく入っていますね。 もちろんDELETEもサポートしてます。

$ curl http://127.0.0.1:8080/rest-sample/books/2 -X DELETE
$ curl http://127.0.0.1:8080/rest-sample/books
<?xml version="1.0" encoding="UTF-8"?><list><book id="1"><title>プログラミングGroovy</title></book></list>

参照専用API

Resourceアノテーションの引数にreadOnly=trueを指定すると、GETによる参照APIのみだけが提供されるようになります。 POSTしても405 Method Not Allowedになります。 情報開示系APIならreadOnlyをつけておくと良さそうです。

コンテントネゴシエーションでレスポンス形式を指定する

デフォルトではレスポンスはXML形式で返ってきますが、拡張子を使ったコンテントネゴシエーションをサポートしているので、URI末尾に.jsonをつけるだけで

$ curl http://127.0.0.1:8080/rest-sample/books.json
[{"class":"rest.sample.Book","id":1,"title":"プログラミングGroovy"}]

JSON形式でレスポンスを受け取ることができます。 ただ、classとか勝手に入っていて内部情報を暴露しすぎでアレなので、ドメインクラス→JSONへのマーシャル方法をカスタマイズした方がよいですね。 方法についてはここでは割愛します。

Content-typeの指定はAcceptヘッダでもOKです。

curl http://127.0.0.1:8080/rest-sample/books -H "Accept:application/json"
[{"class":"rest.sample.Book","id":1,"title":"プログラミングGroovy"}]

まとめ

データ構造を表現するドメインクラスはとりあえず決める必要があるので、ドメインクラスを先に実装するわけです。 で、REST APIとして提供するつもりならサクッとResourceアノテーションAPIを実装しておきつつ、徐々に、制約とかJSONのマーシャライザとかを整えたり、RESTな自前コントローラも追加して、最終的にResoruceアノテーションを削除する、というようなステップを踏めそうなので、そういう意味では初期バージョンの素早いリリースのために使うというのはアリな気がします。

なお、自前のコントローラを実装する場合も、ドメインクラスのオブジェクトとJSON/XMLなどへのコンテントネゴシエーションに基づく変換機能はもちろんそのまま活用できます。

また、Grailsは次のメジャーアップデートである3.0で*2Spring Bootベースに大きく方向転換をする予定なのですが、このようなRESTサポートは多少形を変えつつ残っていくようです。

といったところで。

関連しているようでしていない、ちょっとだけ関連している書籍

プログラミングGROOVY

プログラミングGROOVY

Gradle徹底入門 次世代ビルドツールによる自動化基盤の構築

Gradle徹底入門 次世代ビルドツールによる自動化基盤の構築

*1:プロキシ設定がある場合は、--no-proxyオプションを追加してプロキシをスルーする必要があるかも知れません。

*2:その前に2.5がワンクッション入るかも