Grails RESTful APIの設定方法についてまとめてみた

GrailsでRESTful APIを使用するためにコントローラー使ったり、ドメインに@Resourceアノテーションつけたりいろいろ設定方法があって混乱してたのでまとめました。

はじめに

RESTful APIを使用するために設定すべきことは以下の1つだけです。

  • 公開するurlとそれに対応するコントローラーを指定

公開するurlとそれに対応するコントローラーを指定するには

grails-app/conf/UrlMappings.groovy で指定します。

以下の3行目で指定しています。resourcesの'todo'がTodoControllerに対応するのは規約です。

class UrlMappings {
  static mappings = {
        "/todos"(resources:'todo') // urlとして/todosを公開し、対応するコントローラーとしてTodoControllerを指定

        "/$controller/$action?/$id?(.${format})?"{
            constraints {
                // apply constraints here
            }
        }

        "/"(view:"/index")
        "500"(view:'/error')
    }
}

このようにUrlMappingsに指定することで、HTTPメソッド+リクエストURIで呼び出されるコントローラーのアクションメソッドが対応付けられます。

HTTPメソッド リクエストURI コントローラーのアクションメソッド
GET /todos TodoController#index
GET /todos/create TodoController#create
POST /todos TodoController#save
GET /todos/${id} TodoController#show
GET /todos/${id}/edit TodoController#edit
PUT /todos/${id} TodoController#update
DELETE /todos/${id} TodoController#delete

じゃあドメインクラスに@Resourceアノテーションをつけた場合の動作は?

以下のようにドメインクラスにuriパラメータ有りで@Resourceアノテーションをつければ、UrlMappings.groovyに記述しなくてもRESTful APIを公開可能です。

import grails.rest.Resource
@Resource(uri = "/todos") // urlを指定
class Todo {
    String title
}

@Resource:http://grails.org/doc/latest/api/index.html?grails/rest/Resource.html

これはGrailsが裏側で

  1. ドメイン名Controller(この場合はTodoController)を自動生成
  2. uri パラメータに指定した"/todos"を公開するurlとして、1.で自動生成したコントローラーをurlに対応するコントローラーとしてUrlMappingsに登録

してくれています。

そのため2.のUrlMappingsへの登録は、アノテーションuri指定を省略して直接UrlMappings.groovyに記述することも可能です。

ちょっと話が変わりますが、1.のコントローラーの自動生成についてすでに同名のコントローラーが存在していないかは注意が必要です。

TodoControllerがすでに存在している状態で、Todoドメインに@Resourceアノテーションをつけても正しく動作しません。

これは基本的Grailsはパッケージが異なる同名のコントローラーの存在を許さないからです。

例外としてGrails2.3.3からnamespaceが導入されてパッケージ違いの同名コントローラーが存在できるようになりました

http://grails.org/doc/latest/guide/theWebLayer.html#namespacedControllers

が、@Resourceアノテーションで自動生成されるコントローラーに対してnamespaceを設定する方法が現状(Grails2.3.4)では無いためやはり正しく動作しません。

従って、すでにHTMLを返すコントローラーを作り込んでいる場合に、コントローラーに触りたくないからドメインに@ResourceアノテーションをつけてラクしてRESTful APIを公開しよう!というのは難しいと思います。(タブン・・・)

HTML、JSONXMLなどを適切に返せるようにコントローラーを修正しましょう。

まとめ - RESTful API設定方法

RESTful APIを使用するために設定すべきことは以下の1つだけと書きました。

  • 公開するurlとそれに対応するコントローラーを指定

設定すべきことは1つですが、設定方法としては3種類あります。

urlとコントローラーの対応 コントローラー 選択基準 注意点
1 UrlMappings.groovyに記述 手動で作成 アクションメソッドをカスタマイズ、ネストしたリソースを公開したい 特になし
2 UrlMappings.groovyに記述 @Resourceアノテーションによる自動生成 ネストしたリソースを公開したい 自動生成されるコントローラーと同名のコントローラーが存在していないこと
3 @Resourceアノテーションuriパラメータとして指定 @Resourceアノテーションによる自動生成 1,2に該当しない 自動生成されるコントローラーと同名のコントローラーが存在していないこと

ネストしたリソースをあつかう場合はアクションメソッドをカスタマイズしたいことが多いと思うので 、1を選択するのが良いんじゃないでしょうか。

単純なCRUDに対応するAPIを提供するだけなら3の@Resourceアノテーションを使いましょう。

さいごに

RESTful APIの設定方法について調べてまとめてみました。

気になるところ、間違っているところがあればコメントください。

Backbone.js版ToDoMVCアプリのバックエンドをGrails REST APIに変更する

Grails2.3から加わったRESTful APIを提供する機能を使って、Backbone.js版ToDoMVCアプリのバックエンドをlocalStorageからGrails REST APIに変更してみます。

出来たやつ:https://github.com/geekduck/restapp のcommit 4f577884f97db839eb986385c2ed3bb4e96aca2b

環境

  • Grails 2.3.4
  • ToDoMVC 1.2.0
  • Backbone.js 1.0.0

Grailsアプリを作成

grails create-app restapp

ToDoMVCアプリの配置、起動確認

http://todomvc.com/ からアプリをダウンロードし、適当な場所に展開します。(ここでは~/Downloads/tastejs-todomvc-b29e998に展開しました)

Backbone.js版のToDoMVCアプリをGrailsアプリのweb-app/backboneに配置します。

cp ~/Downloads/tastejs-todomvc-b29e998/architecture-examples/backbone restapp/web-app

Grailsを起動し、ToDoMVCアプリを実行してみます。

cd restapp
grails

grails> run-app

http://localhost:8080/restapp/backbone/ にアクセスすると、ToDoMVCアプリが表示されます。

この段階ではブラウザのlocalStorageにtodoのデータが格納されます。以降でバックエンドをGrailsのRESTful APIで置き換えていきます。

ToDoドメインを作成、RESTful APIとして公開

クライアント側のモデル(web-app/backbone/js/models/todo.js)に対応するドメインGrails側で作成します。

Grailsが起動中の場合は停止しておきます。

grails> stop-app
grails> create-domain-class Todo

作成したToDoドメインを編集します。

package restapp

import grails.rest.Resource

@Resource(uri='/todos', formats=['json', 'xml'])
class Todo {
    String title
    Boolean completed

    static constraints = {
    }
}

ここでドメインクラスに@Resourceアノテーションを付与しておくと、以下の表のようなURIマッピングと各URIに対応するController Actionを自動で作成してくれます。

HTTP Method URI Controller Action
GET /todos index
GET /todos/create create
POST /todos save
GET /todos/${id} show
GET /todos/${id}/edit edit
PUT /todos/${id} update
DELETE /todos/${id} delete

RESTful APIのテストデータをBootStrap.groovyに追加しておきます。

import restapp.Todo

class BootStrap {

    def init = { servletContext ->
        new Todo(title:"aaa", completed: false).save()
        new Todo(title:"bbb", completed: false).save()
    }
    def destroy = {
    }
}

Grailsアプリを起動してブラウザでhttp://localhost:8080/restapp/todos にアクセスすると、Bootstrap.groovyで追加したテストデータのJSONリストが表示されます。

サーバー側の準備はこれで完了です。

ToDoMVCアプリのバックエンドをRESTful APIに修正

Todo Collection(web-app/backbone/js/collections/todos.js)からlocalStorageを使用している一行を削除し、サーバー側のRESTful APIURIを返すurlメソッドを定義します。

diff --git a/web-app/backbone/js/collections/todos.js b/web-app/backbone/js/collections/todos.js
index db7b6a5..739c21b 100755
--- a/web-app/backbone/js/collections/todos.js
+++ b/web-app/backbone/js/collections/todos.js
@@ -7,14 +7,16 @@ var app = app || {};
     // Todo Collection
     // ---------------

-    // The collection of todos is backed by *localStorage* instead of a remote
-    // server.
+    // The collection of todos is backed by grails server.
     var Todos = Backbone.Collection.extend({
         // Reference to this collection's model.
         model: app.Todo,

-        // Save all of the todo items under the `"todos"` namespace.
-        localStorage: new Backbone.LocalStorage('todos-backbone'),
+        // REST API URL
+        url: function () {
+            return '/restapp/todos';
+        },
+

         // Filter down the list of all todo items that are finished.
         completed: function () {
~

クライアント側で変更しなければならない点は上記の1箇所だけですが、web-app/backbone/index.html から不要なJS(base.js、backbone.localStorage.js)を参照したままなので削除しておきます。

diff --git a/web-app/backbone/index.html b/web-app/backbone/index.html
index 745960d..31c0df7 100755
--- a/web-app/backbone/index.html
+++ b/web-app/backbone/index.html
@@ -51,11 +51,9 @@
     <button id="clear-completed">Clear completed (<%= completed %>)</button>
     <% } %>
 </script>
-<script src="bower_components/todomvc-common/base.js"></script>
 <script src="bower_components/jquery/jquery.js"></script>
 <script src="bower_components/underscore/underscore.js"></script>
 <script src="bower_components/backbone/backbone.js"></script>
-<script src="bower_components/backbone.localStorage/backbone.localStorage.js"></script>
 <script src="js/models/todo.js"></script>
 <script src="js/collections/todos.js"></script>
 <script src="js/views/todo-view.js"></script>

これでクライアント側の修正は完了です。

動作確認

grailsを起動して、http://localhost:8080/restapp/backbone/ にアクセスします。

Bootstrap.groovyで追加したテストデータが最初から表示されているはずです。

データベースを確認してみましょう。

http://localhost:8080/restapp/dbconsole/ をブラウザで開き、JDBC URLに「jdbc:h2:mem:devDb」を入力しConnectします。

そこからToDoドメインに対応するデータベースのテーブルを参照できるので、クライアント側で新規作成や更新、削除を行いデータベースが更新されているのを確認します。

まとめ

今回のToDoドメインのように単純なCRUD操作を行うようなAPIであれば、Grailsの場合は5行程度でRESTful APIを公開できるので楽で良いですね!

参考資料

Grails本家:http://grails.org/doc/latest/guide/webServices.html#REST

上記の日本語訳:http://grails.jp/doc/latest/guide/webServices.html#REST

id:yamkazuさんのハンズオン資料:https://github.com/yamkazu/jggug-nov-2013-grails23