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が裏側で
- ドメイン名Controller(この場合はTodoController)を自動生成
- 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、JSON、XMLなどを適切に返せるようにコントローラーを修正しましょう。
まとめ - 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 APIのURIを返す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