8 Webサービス - Reference Documentation
Authors: Graeme Rocher, Peter Ledbrook, Marc Palmer, Jeff Brown, Luke Daley, Burt Beckwith
Version: 2.3.0
Translated by: T.Yamamoto, Japanese Grails Doc Translating Team. Special thanks to NTT Software.
【注意】このドキュメントの内容はスナップショットバージョンを元に*意訳*されているため、一部現行バージョンでは未対応の機能もあります。
Table of Contents
8 Webサービス
Web Services are all about providing a web API onto your web application and are typically implemented in either REST or SOAP
WebサービスはWebアプリケーションにWeb APIを提供するものであり、通常はSOAPまたはRESTで実装されています。
8.1 REST
REST is not really a technology in itself, but more an architectural pattern. REST is very simple and just involves using plain XML or JSON as a communication medium, combined with URL patterns that are "representational" of the underlying system, and HTTP methods such as GET, PUT, POST and DELETE.
RESTは本来SOAPのようなプロトコルではなく、アーキテクチャスタイルです。RESTは非常に単純です。簡素なXMLやJSONを通信媒体に用いて、基本的なシステムとGET、PUT、POSTおよびDELETEといったHTTPメソッドを「表現する」URLパターンを組み合わせています。Each HTTP method maps to an action type. For example GET for retrieving data, POST for creating data, PUT for updating and so on.
それぞれのHTTPメソッドは異なるアクションにマップします。例えば、GETはデータ検索、PUTはデータ作成、POSTは更新などとマップします。Grails includes flexible features that make it easy to create RESTful APIs. Creating a RESTful resource can be as simple as one line of code, as demonstrated in the next section.
8.1.1 RESTリソースとしてドメインクラスを使用する
The easiest way to create a RESTful API in Grails is to expose a domain class as a REST resource. This can be done by adding the
GrailsでRESTFul APIを簡単に作成する方法は、ドメインクラスをRESTリソースとして公開することです。
これは、任意のドメインクラスにgrails.rest.Resource
transformation to any domain class:
grails.rest.Resource
変換を追加するだけです:import grails.rest.*@Resource(uri='/books') class Book { String title static constraints = { title blank:false } }
Simply by adding the
単にResource
transformation and specifying a URI, your domain class will automatically be available as a REST resource in either XML or JSON formats. The transformation will automatically register the necessary RESTful URL mapping and create a controller called BookController
.
Resource
変換を追加してURIを指定することで、ドメインクラスは自動的にXML、JSONフォーマットをサポートするRESTリソースとして使用可能になります。
この変換は、必要なRESTful URL マッピングを自動的に登録し、BookController
と呼ばれるコントローラを作成します。
You can try it out by adding some test data to
いくつかのテストデータをBootStrap.groovy
:
BootStrap.groovy
に追加することで動作確認ができます。def init = { servletContext -> new Book(title:"The Stand").save() new Book(title:"The Shining").save() }
And then hitting the URL http://localhost:8080/myapp/books/1, which will render the response like:
そして、http://localhost:8080/myapp/books/1
のURLをたたいてみると、以下のようなレスポンスが表示されます:<?xml version="1.0" encoding="UTF-8"?> <book id="1"> <title>The Stand</title> </book>
If you change the URL to
URLをhttp://localhost:8080/myapp/books/1.json
you will get a JSON response such as:
http://localhost:8080/myapp/books/1.json
に変更すると、以下のようなJSONレスポンスが得られます:
{"id":1,"title":"The Stand"}
If you wish to change the default to return JSON instead of XML, you can do this by setting the
XMLの代わりにデフォルトでJSONを返したい場合は、formats
attribute of the Resource
transformation:
Resource
変換のformats
属性を設定します。 import grails.rest.*@Resource(uri='/books', formats=['json', 'xml']) class Book { … }
With the above example JSON will be prioritized. The list that is passed should contain the names of the formats that the resource should expose. The names of formats are defined in the
上記の例ではJSONを優先するように設定しています。
この一覧には公開するフォーマットの名前を含む必要があります。
フォーマットの名前はgrails.mime.types
setting of Config.groovy
:
Config.groovy
のgrails.mime.types
設定の中で定義されます:grails.mime.types = [ … json: ['application/json', 'text/json'], … xml: ['text/xml', 'application/xml'] ]
See the section on Configuring Mime Types in the user guide for more information.
さらなる情報はユーザガイド内のMIMEタイプの定義を参照してください。
Instead of using the file extension in the URI, you can also obtain a JSON response using the ACCEPT header. Here's an example using the Unix
URIでファイル拡張子を使う代わりに、ACCEPTヘッダを使ってJSONレスポンスを得ることもできます。
これはUnixのcurl
tool:
curl
コマンドを使用した例です:$ curl -i -H "Accept: application/json" localhost:8080/myapp/books/1 {"id":1,"title":"The Stand"}
This works thanks to Grails' Content Negotiation features.
これはGrailsのコンテントネゴシエーション機能のおかげで動作しています。
You can create a new resource by issuing a
POST
request:
POST
リクエスを発行することで新たなリソースを作成できます:$ curl -i -X POST -H "Content-Type: application/json" -d '{"title":"Along Came A Spider"}' localhost:8080/myapp/books HTTP/1.1 201 Created Server: Apache-Coyote/1.1 ...
Updating can be done with a
更新にはPUT
request:
PUT
リクエストを使用します:$ curl -i -X PUT -H "Content-Type: application/json" -d '{"title":"Along Came A Spider"}' localhost:8080/myapp/books/1 HTTP/1.1 200 OK Server: Apache-Coyote/1.1 ...
Finally a resource can be deleted with
最後に、DELETE
request:
DELETE
リクエストでリソースが削除されます:$ curl -i -X DELETE localhost:8080/myapp/books/1 HTTP/1.1 204 No Content Server: Apache-Coyote/1.1 ...
As you can see, the
ここまで見てきたように、Resource
transformation enables all of the HTTP method verbs on the resource. You can enable only read-only capabilities by setting the readOnly
attribute to true:
Resource
変換はリソースに対してHTTPメソッドに対応する動作のすべてを有効にします。
readOnly
属性をtrueに設定することで読み取り専用にできます:import grails.rest.*@Resource(uri='/books', readOnly=true) class Book { … }
In this case POST, PUT and DELETE requests will be forbidden.
この場合は、POST、PUT、そしてDELETEリクエストを受付けません。
8.1.2 RESTリソースへのマッピング
If you prefer to keep the declaration of the URL mapping in your
もしUrlMappings.groovy
file then simply removing the uri
attribute of the Resource
transformation and adding the following line to UrlMappings.groovy
will suffice:
UrlMappings.groovy
内にURLマッピングの宣言を保持したい場合は、単にResource
変換からuri
属性を削除して、UrlMappings.groovy
へ以下の行を追加するだけです:"/books"(resources:"book")
Extending your API to include more end points then becomes trivial:
APIを拡張して、さらにエンドポイントを追加したい場合は以下のようにします:"/books"(resources:"book") { "/publisher"(controller:"publisher", method:"GET") }
The above example will expose the URI
上記の例は/books/1/publisher
.
/books/1/publisher
というURIで公開されます。
A more detailed explanation on creating RESTful URL mappings can be found in the URL Mappings section of the user guide.
RESTful URLマッピングの作成についてのより詳細な説明は、ユーザガイドのURLマッピング内にあります。
8.1.3 RESTリソースへのリンク
The
link
tag offers an easy way to link to any domain class resource:
link
タグは、任意のドメインクラスリソースへリンクする簡単な方法を提供します:<g:link resource="${book}">My Link</g:link>
8.1.4 RESTリソースのバージョニング
A common requirement with a REST API is to expose different versions at the same time. There are a few ways this can be achieved in Grails.
REST APIの一般的な要件として、同時に異なるバージョンの公開があります。
Grailsでこれを実現するにはいくつかの方法があります。h4. Versioning using the URI
URIを使ったバージョンニング
A common approach is to use the URI to version APIs (although this approach is discouraged in favour of Hypermedia). For example, you can define the following URL mappings:
一般的な方法は、各バージョンのAPIへのURIを使うことです(しかし、この方法よりはハイパーメディアを使った方法をお勧めします)。
例えば、以下のようにURLマッピングを定義できます:"/books/v1"(resources:"book", namespace:'v1') "/books/v2"(resources:"book", namespace:'v2')
That will match the following controllers:
これは以下のコントローラに対応付きます:package myapp.v1class BookController { static namespace = 'v1' }package myapp.v2class BookController { static namespace = 'v2' }
This approach has the disadvantage of requiring two different URI namespaces for your API.
この方法は、APIに対して異なる2つのURIのネームスペースが必要となるという欠点があります。h4. Versioning with the Accept-Version header
Accept-Versionヘッダを使ったバージョンニング
As an alternative Grails supports the passing of an
代わりに、GrailsはクライアントからのAccept-Version
header from clients. For example you can define the following URL mappings:Accept-Version
ヘッダによる制御をサポートしています。
例えば以下のようにURLマッピングを定義できます:"/books"(version:'1.0', resources:"book", namespace:'v1') "/books"(version:'2.0', resources:"book", namespace:'v2')
Then in the client simply pass which version you need using the
そして、クライアントからはAccept-Version
header:Accept-Version
ヘッダを使ってバージョンを渡すだけです:$ curl -i -H "Accept-Version: 1.0" -X GET http://localhost:8080/myapp/books
h4. Versioning using Hypermedia / Mime Types
ハイパーメディア/MIMEタイプを使ったバージョニング
Another approach to versioning is to use Mime Type definitions to declare the version of your custom media types (see the section on "Hypermedia as the Engine of Application State" for more information about Hypermedia concepts). For example, in
他のバージョニング方法は、MIMEタイプ定義を使い、カスタムメディアタイプとしてバージョンを宣言する方法です(ハイパーメディアのコンセプトについての、より詳しい情報は「アプリケーション状態エンジンとしてのハイパーメディア」を参照してください)。
例えばConfig.groovy
you can declare a custom Mime Type for your resource that includes a version parameter (the 'v' parameter):
Config.groovy
内に、リソースに対してバージョンパラメータ('v'のパラメータ)を含んだカスタムMIMEタイプを宣言できます:grails.mime.types = [ all: '*/*', book: "application/vnd.books.org.book+json;v=1.0", bookv2: "application/vnd.books.org.book+json;v=2.0", … }
It is critical that place your new mime types after the 'all' Mime Type because if the Content Type of the request cannot be established then the first entry in the map is used for the response. If you have your new Mime Type at the top then Grails will always try and send back your new Mime Type if the requested Mime Type cannot be established.
'all'のMIMEタイプの後に、新たなMIMEタイプを宣言しているのには重要な意味があります。 なぜなら、リクエストのコンテンツタイプが不明な場合は、マップ内の最初のエントリがレスポンスのコンテンツタイプとして使われるためです。 もし、新たなMIMEタイプが先頭にあるとすると、リクエストのコンテンツタイプが不明な場合に、Grailsは新たなMIMEタイプを常に送り返すことになります。
Then override the renderer (see the section on "Customizing Response Rendering" for more information on custom renderers) to send back the custom Mime Type in
次に、grails-app/conf/spring/resourses.groovy
:grails-app/conf/spring/resourses.groovy
内でカスタムMIMEタイプを返すようにレンダリング設定(カスタムレンダリングのより詳しい情報は「レスポンスレンダリングのカスタマイズ」を参照してください)を上書きします:import grails.rest.renderer.json.* import org.codehaus.groovy.grails.web.mime.*beans = { bookRendererV1(JsonRenderer, myapp.v1.Book, new MimeType("application/vnd.books.org.book+json", [v:"1.0"])) bookRendererV2(JsonRenderer, myapp.v2.Book, new MimeType("application/vnd.books.org.book+json", [v:"2.0"])) }
Then using the
これで、Accept
header you can specify which version you need using the Mime Type:Accept
ヘッダでMIMEタイプを使いバージョンを指定できます。$ curl -i -H "Accept: application/vnd.books.org.book+json;v=1.0" -X GET http://localhost:8080/myapp/books
8.1.5 RESTコントローラの実装
The
Resource
transformation is a quick way to get started, but typically you'll want to customize the controller logic, the rendering of the response or extend the API to include additional actions.Resource
変換はREST APIを構築する手っ取り早い方法です。
しかし大抵の場合、レスポンスのレンダリングを変更したり、APIを拡張するなど、コントローラのロジックをカスタマイズしたくなります。
8.1.5.1 RestfulControllerスーパークラスの拡張
The easiest way to get started doing so is to create a new controller for your resource that extends the
まず始めの簡単な方法は、grails.rest.RestfulController
super class. For example:grails.rest.RestfulController
をスーパークラスとして、リソースに対して新たなコントローラを作成する方法です。
例えば:class BookController extends RestfulController { static responseFormats = ['json', 'xml'] BookController() { super(Book) } }
To customize any logic you can just override the appropriate action. The following table provides the names of the action names and the URIs they map to:
ロジックをカスタマイズするには、カスタマイズが必要なアクションを上書きします。
以下のテーブルは、アクションの名前と、それに対応するURIです:HTTP Method | URI | Controller Action |
---|---|---|
GET | /books | index |
GET | /books/create | create |
POST | /books | save |
GET | /books/${id} | show |
GET | /books/${id}/edit | edit |
PUT | /books/${id} | update |
DELETE | /books/${id} | delete |
Note that thecreate
andedit
actions are only needed if the controller exposes an HTML interface.
create
とedit
アクションは、コントローラがHTMLインタフェースを公開する場合にのみ必要です。
As an example, if you have a nested resource then you would typically want to query both the parent and the child identifiers. For example, given the following URL mapping:
例のようにネストしたリソースの場合、大抵は親と子供の識別子が必要になります。
例えば以下のようなURLマッピングの場合:"/authors"(resources:'author') { "/books"(resources:'book') }
You could implement the nested controller as follows:
次のように、ネストに対応したコントローラを実装できます:class BookController extends RestfulController { static responseFormats = ['json', 'xml'] BookController() { super(Book) } @Override Book queryForResource(Serializable id) { Book.where { id == id && author.id = params.authorId }.find() }}
The example above subclasses
上記の例では、RestfulController
and overrides the queryForResource
method to customize the query for the resource to take into account the parent resource.RestfulController
のサブクラスで親リソースに対応するため、queryForResource
メソッドをオーバーライドしてリソースのクエリをカスタマイズしています。
8.1.5.2 RESTコントローラの実装ステップバイステップ
If you don't want to take advantage of the features provided by the
もし、スーパークラスのRestfulController
super class, then you can implement each HTTP verb yourself manually. The first step is to create a controller:RestfulController
から提供される機能を使用しない場合は、それぞれHTTPメソッドに対応するアクションを自身で実装できます。
最初のステップはコントローラの作成です:$ grails create-controller book
Then add some useful imports and enable readOnly by default:
そして、いくつかの便利なインポートを追加し、デフォルトで読み取り専用にします:import grails.transaction.* import static org.springframework.http.HttpStatus.* import static org.springframework.http.HttpMethod.*@Transactional(readOnly = true) class BookController { … }
Recall that each HTTP verb matches a particular Grails action according to the following conventions:
HTTPメソッドのアクションは、以下の規約に従って、それぞれGrailsのアクションに対応付くことを思い出してください:
HTTP Method | URI | Controller Action |
---|---|---|
GET | /books | index |
GET | /books/${id} | show |
GET | /books/create | create |
GET | /books/${id}/edit | edit |
POST | /books | save |
PUT | /books/${id} | update |
DELETE | /books/${id} | delete |
The 'create' and 'edit' actions are already required if you plan to implement an HTML interface for the REST resource. They are there in order to render appropriate HTML forms to create and edit a resource. If this is not a requirement they can be discarded.
'create'と'edit'アクションはRESTリソースに対して、HTMLインタフェースを実装する予定がある場合は必要です。 これらはリソースの追加、編集のHTMLフォームを表示するためにあります。 これが必要ない場合は無効にできます。
The key to implementing REST actions is the respond method introduced in Grails 2.3. The
RESTアクションの実装の鍵は、Grails 2.3で導入されたrespondメソッドです。
respond
method tries to produce the most appropriate response for the requested content type (JSON, XML, HTML etc.)respond
メソッドは、リクエストのContent Type(JSON、XML、HTMLなど)に対して、もっとも適切なレスポンスを生成しようとします。h4. Implementing the 'index' action
'index'アクションの実装
For example, to implement the
例えばindex
action, simply call the respond
method passing the list of objects to respond with:
index
アクションを実装するには、単にオブジェクトのリストと共にrespond
メソッドを呼びます:def index(Integer max) { params.max = Math.min(max ?: 10, 100) respond Book.list(params), model:[bookCount: Book.count()] }
Note that in the above example we also use the
上記の例では、オブジェクトのリストに加えて、合計値を設定するためにmodel
argument of the respond
method to supply the total count. This is only required if you plan to support pagination via some user interface.respond
メソッドにmodel
引数を渡しています。
これは、ユーザインタフェースでページングをサポートする場合にのみ必要です。The
respond
method will, using Content Negotiation, attempt to reply with the most appropriate response given the content type requested by the client (via the ACCEPT header or file extension).respond
メソッドは、コンテントネゴシエーションを使って、クライントからリクエストされたコンテンツタイプから(ACCEPTヘッダまたはファイル拡張子から)、もっとも適切なレスポンスを返そうとします。If the content type is established to be HTML then a model will be produced such that the action above would be the equivalent of writing:
もしコンテンツタイプがHTMLである場合は、上記のアクションは次のように書いた場合と同じようなmodel
を生成します:def index(Integer max) { params.max = Math.min(max ?: 10, 100) [bookList: Book.list(params), bookCount: Book.count()] }
By providing an
そして、index.gsp
file you can render an appropriate view for the given model. If the content type is something other than HTML then the respond
method will attempt to lookup an appropriate grails.rest.render.Renderer
instance that is capable of rendering the passed object. This is done by inspecting the grails.rest.render.RendererRegistry
.index.gsp
ファイルを提供することで、与えられたmodel
に対するビューが表示できます。
もし、コンテンツタイプがHTMLではない場合、respond
メソッドは渡されたオブジェクトをレンダリングできる適切なgrails.rest.render.Renderer
のインスタンスを探します。
これはgrails.rest.render.RendererRegistry
から検索されます。By default there are already renderers configured for JSON and XML, to find out how to register a custom renderer see the section on "Customizing Response Rendering".
デフォルトではJSONとXMLのレンダラーが設定されています。
カスタムレンダラーの登録の仕方は「レスポンスレンダリングのカスタマイズ」を参照してください。h4. Implementing the 'show' action
'show'アクションの実装
The
show
action, which is used to display and individual resource by id, can be implemented in one line of Groovy code (excluding the method signature):show
アクションはidによって個々のリソースを表示するためのものです。
これはGroovyコード1行で実装できます(メソッドのシグネチャを除く):def show(Book book) { respond book }
By specifying the domain instance as a parameter to the action Grails will automatically attempt to lookup the domain instance using the
アクションパラメータとして特定のドメインインスタンスを指定すると、Grailsは自動的にリクエストのid
parameter of the request. If the domain instance doesn't exist, then null
will be passed into the action. The respond
method will return a 404 error if null is passed otherwise once again it will attempt to render an appropriate response. If the format is HTML then an appropriate model will produced. The following action is functionally equivalent to the above action:
id
パラメータからドメインインスタンスを検索しようとします。
もし、ドメインインスタンスが存在しない場合は、アクションへnull
が渡されます。
respond
メソッドはnullが渡された場合は404エラーを返します。
nullではない場合は、前と同じように適切なレスポンスを表示しようとします。
もし、フォーマットがHTMLの場合は適切なmodel
を生成します。
以下のアクションは、上記のアクションと機能的に同じです:def show(Book book) { if(book == null) { render status:404 } else { return [book: book] } }
h4. Implementing the 'save' action
'save'アクションの実装
The
save
action creates new resource representations. To start off, simply define an action that accepts a resource as the first argument and mark it as Transactional
with the grails.transaction.Transactional
transform:
save
アクションは新たなリソースを保存します。
save
アクションを簡単に実装するには、最初の引数にリソースをとるアクションを定義し、そのアクションにgrails.transaction.Transactional
変換を使ってTransactional
としてマークします:@Transactional def save(Book book) { … }
Then the first thing to do is check whether the resource has any validation errors and if so respond with the errors:
そして、最初にすべきことはリソースにバリデーションエラーがないかチェックすることです。
エラーがある場合はerrors
を応答します:if(book.hasErrors()) { respond book.errors, view:'create' } else { … }
In the case of HTML the 'create' view will be rendered again so the user can correct the invalid input. In the case of other formats (JSON, XML etc.), the errors object itself will be rendered in the appropriate format and a status code of 422 (UNPROCESSABLE_ENTITY) returned.
HTMLの場合は'create'ビューが再度表示されます。
そして、ユーザは誤った入力を修正できます。
それ以外のフォーマット(JSON、XMLなど)の場合は、エラーオブジェクト自身が適切なフォーマットで表示され、422(UNPROCESSABLE_ENTITY)のステータスコードが返されます。If there are no errors then the resource can be saved and an appropriate response sent:
もし、エラーがない場合は、リソースが保存され、適切なレスポンスが返されます:book.save flush:true withFormat { html { flash.message = message(code: 'default.created.message', args: [message(code: 'book.label', default: 'Book'), book.id]) redirect book } '*' { render status: CREATED } }
In the case of HTML a redirect is issued to the originating resource and for other formats a status code of 201 (CREATED) is returned.
HTMLの場合は、オリジナルのリソースへリダイレクトされます。
そして、他のフォーマットでは201(CREATED)のステータスコードが返されます。h4. Implementing the 'update' action
'update'アクションの実装
The
update
action updates an existing resource representations and is largely similar to the save
action. First define the method signature:
update
アクションは既存のリソースを更新します。
そして、update
アクションはsave
アクションとほとんど同じです。
はじめにメソッドのシグネチャを定義します:@Transactional def update(Book book) { … }
If the resource exists then Grails will load the resource, otherwise null we passed. In the case of null, you should return a 404:
もしリソースが存在する場合は、Grailsはリソースを読み込み、そうでない場合はnullを渡します。
nullの場合は、404を返すべきです:if(book == null) { render status: NOT_FOUND } else { … }
Then once again check for errors validation errors and if so respond with the errors:
そして、前と同じようにerrorsに対してバリデーションエラーをチェックして、エラーがある場合はerrors
を応答します:if(book.hasErrors()) { respond book.errors, view:'edit' } else { … }
In the case of HTML the 'edit' view will be rendered again so the user can correct the invalid input. In the case of other formats (JSON, XML etc.) the errors object itself will be rendered in the appropriate format and a status code of 422 (UNPROCESSABLE_ENTITY) returned.
HTMLの場合は'edit'ビューが再度表示されます。
そして、ユーザは誤った入力を修正できます。
それ以外のフォーマット(JSON、XMLなど)の場合は、エラーオブジェクト自身が適切なフォーマットで表示され、422(UNPROCESSABLE_ENTITY)のステータスコードが返されます。If there are no errors then the resource can be saved and an appropriate response sent:
もし、エラーがない場合は、リソースが保存され、適切なレスポンスが返されます:book.save flush:true withFormat { html { flash.message = message(code: 'default.updated.message', args: [message(code: 'book.label', default: 'Book'), book.id]) redirect book } '*' { render status: OK } }
In the case of HTML a redirect is issued to the originating resource and for other formats a status code of 200 (OK) is returned.
HTMLの場合は、オリジナルのリソースへリダイレクトされ、他のフォーマットでは200(OK)のステータスコードが返されます。h4. Implementing the 'delete' action
'delete'アクションの実装
The
delete
action deletes an existing resource. The implementation is largely similar to the update
action, expect the delete()
method is called instead:delete
アクションは既存のリソースを削除します。
実装は、update
アクションとほとんど同じでdelete()
メソッドを代わりに呼びます:book.delete flush:true withFormat { html { flash.message = message(code: 'default.deleted.message', args: [message(code: 'Book.label', default: 'Book'), book.id]) redirect action:"index", method:"GET" } '*'{ render status: NO_CONTENT } }
Notice that for an HTML response a redirect is issued back to the
HTMLレスポンスではindex
action, whilst for other content types a response code 204 (NO_CONTENT) is returned.index
アクションへリダイレクトされます。
他のコンテンツタイプに対しては、レスポンスコード204(NO_CONTENT)が返されます。
8.1.5.3 スカッフォルドを使用してRESTコントローラを生成
To see some of these concepts in action and help you get going the Scaffolding plugin, version 2.0 and above, can generate a REST ready controller for you, simply run the command:
アクション内でここまで見てきたコンセプトを得るには、バージョン2.0以上のスカッフォルドプラグインが役に立ちます。
このプラグインは、REST機能が組み込まれたコントローラを生成できます。
これは単にコマンドを実行するだけです:$ grails generate-controller [Domain Class Name]
8.1.6 レスポンスレンダリングのカスタマイズ
There are several ways to customize response rendering in Grails.
Grailsではレスポンスのレンダリングをカスタマイズする方法がいくつかあります。
8.1.6.1 デフォルトレンダラーのカスタマイズ
The default renderers for XML and JSON can be found in the
XMLとJSONに対するデフォルトのレンダラーは、それぞれgrails.rest.render.xml
and grails.rest.render.json
packages respectively. These use the Grails converters (grails.converters.XML
and grails.converters.JSON
) by default for response rendering.
grails.rest.render.xml
、grails.rest.render.json
パッケージ内にあります。
これらはレスポンスのレンダリングにGrailsのコンバータ(grails.converters.XML
とgrails.converters.JSON
)を使います。
You can easily customize response rendering using these default renderers. A common change you may want to make is to include or exclude certain properties from rendering.
これらのデフォルトレンダラーを使って簡単にレスポンスのレンダリングをカスタマイズできます。
よく必要となる変更は、レンダリング時に特定のプロパティをインクルード、またはエクスクルードすることです。h4. Including or Excluding Properties from Rendering
レンダリング時のプロパティのインクルードとエクスクルード
As mentioned previously, Grails maintains a registry of
前に説明したように、Grailsはgrails.rest.render.Renderer
instances. There are some default configured renderers and the ability to register or override renderers for a given domain class or even for a collection of domain classes. To include a particular property from rendering you need to register a custom renderer by defining a bean in grails-app/conf/spring/resources.groovy
:grails.rest.render.Renderer
インスタンスをレジストリで管理しています。
デフォルトで設定されているレンダラーがいくつかありますが、与えられたドメインクラス、またはドメインクラスのコレクションに対し、レンダラーの登録や上書きが可能です。
レンダリングで特定のプロパティをインクルードするには、grails-app/conf/spring/resources.groovy
内のビーン定義によって、カスタムレンダラーを登録する必要があります:import grails.rest.render.xml.*beans = { bookRenderer(XmlRenderer, Book) { includes = ['title'] } }
The bean name is not important (Grails will scan the application context for all registered renderer beans), but for organizational and readability purposes it is recommended you name it something meaningful.
ビーンの名前は重要ではありません(Grailsは登録されたすべてのレンダラーのビーンを見つけるために、アプリケーションコンテキストをスキャンします)。 しかし、構造と可読性の理由から、分かりやすい名前を付けておくことをお勧めします。
To exclude a property, the
プロパティをエクスクルードするには、excludes
property of the XmlRenderer
class can be used:
XmlRenderer
クラスのexclude
プロパティを使います:import grails.rest.render.xml.*beans = { bookRenderer(XmlRenderer, Book) { excludes = ['isbn'] } }
h4. Customizing the Converters
コンバーターのカスタマイズ
As mentioned previously, the default renders use the
前に説明したように、デフォルトのレンダラーはgrails.converters
package under the covers. In other words, under the covers they essentially do the following:
grails.converters
パッケージ配下のクラスを使っています。
これは、パッケージ配下のクラスを使って以下のようにした場合と本質的には同じです:import grails.converters.*…
render book as XML// or render book as JSON
Why the separation between converters and renderers? Well a renderer has more flexibility to use whatever rendering technology you chose. When implementing a custom renderer you could use Jackson, Gson or any Java library to implement the renderer. Converters on the other hand are very much tied to Grails' own marshalling implementation.
なぜコンバーターとレンダラーが分かれているのでしょうか?
これは、レンダラーにおいて任意のレンダリング技術を採用できるよう、より柔軟性を持たせるためです。
これにより、カスタムレンダラーを実装する際に、JacksonやGson、または他のJavaライブラリをレンダラーを実装するために使用できます。
8.1.6.2 カスタムオブジェクトマーシャラの登録
Grails' Converters feature the notion of an ObjectMarshaller and each type can have a registered
Grailsのコンバーターは、オブジェクトマーシャラの概念を特徴としており、それぞれの型に対してObjectMarshaller
. You can register custom ObjectMarshaller
instances to completely customize response rendering. For example, you can define the following in BootStrap.init
:
ObjectMarshaller
を登録できます。
そして、レスポンスのレンダリングをカスタマイズするためにカスタムObjectMarshaller
インタンスを登録できます。
例えば、BootStrap.init
の中で以下のように定義できます:XML.registerObjectMarshaller Book, { Book book, XML xml -> xml.attribute 'id', book.id xml.build { title(book.title) } }
You can customize the formatting of an indvidual value this way too. For example the JodaTime plugin does the following to support rendering of JodaTime dates in JSON output:
個々の値のフォーマットをカスタマイズする場合もこの方法で可能です。
例えば、JodaTime pluginは、JSON出力でJadaTime日付のレンダリングをサポートするために次のようにしています:JSON.registerObjectMarshaller(DateTime) { return it?.toString("yyyy-MM-dd'T'HH:mm:ss'Z'") }
In the case of JSON it's often simple to use a map to customize output:
JSONの場合は、マップを使って簡単に出力をカスタマイズできます:JSON.registerObjectMarshaller(Book) {
def map= [:]
map['titl'] = it.title
map['auth'] = it.author
return map
}
h4. Registering Custom Marshallers via Spring
Spring経由でカスタムマーシャラを登録する
Note that if you have many custom marshallers it is recommended you split the registration of these into a separate class:
もし多くのカスタムマーシャラがある場合は、マーシャラの登録をそれぞれ別々のクラスへ分けることがお勧めです:class CustomMarshallerRegistrar { @javax.annotation.PostConstruct void registerMarshallers() { JSON.registerObjectMarshaller(DateTime) { return it?.toString("yyyy-MM-dd'T'HH:mm:ss'Z'") } } }
Then define this class as Spring bean in
そして、このクラスをgrails-app/conf/spring/resources.groovy
:grails-app/conf/spring/resources.groovy
内でSpringビーンとして定義します:beans = { myCustomMarshallerRegistrar(CustomMarshallerRegistrar) }
The
PostConstruct
annotation will get triggered on startup of your application.PostConstruct
アノテーションはアプリケーション起動時に処理を起動するトリガーになります。
8.1.6.3 オブジェクト・マーシャラの名前付き定義
It is also possible to register named configurations. For example:
名前付きの設定で登録することもできます。例えば:XML.createNamedConfig('publicApi') { it.registerObjectMarshaller(Book) { Book book, XML xml -> // do public API } } XML.createNamedConfig('adminApi') { it.registerObjectMarshaller(Book) { Book book, XML xml -> // do admin API } }
Then when you use either the
そして、もしリクエストごとのレンダリングのカスタマイズが必要な場合に、render
or respond
methods you can wrap the call in a named configuration if necessary to customize rendering per request:render
またはrespond
メソッドのどちらかを使い、名前付き設定でそのメソッド呼び出しをラップできます。XML.use( isAdmin ? 'adminApi' : 'publicApi') { render book as XML }
or
またはXML.use( isAdmin ? 'adminApi' : 'publicApi') { respond book }
8.1.6.4 ObjectMarshallerインターフェイスへの実装
For more complex marshallers it is recommended you implement the ObjectMarshaller interface. For example given a domain class:
より複雑なマーシャラに対しては、ObjectMarshallerインタフェースの実装がお勧めです。class Book {
String title
}
By default the output when using:
デフォルトのマーシャラを使ったときの出力は:render book as XML
Would look like:
次のようになります:<book id="1"> <title>The Stand</title> </book>
To write a custom marshaller you can do the following:
カスタムマーシャラを記述するには以下のようにします:class BookMarshaller implements ObjectMarshaller<XML> { public boolean supports(Object object) { return object instanceof Book } public void marshalObject(Object object, XML converter) { Book book = (Book)object converter.chars book.title } }
And then register the marshaller with:
そして、そのマーシャラを登録します:XML.registerObjectMarshaller(new BookMarshaller())
With the custom
カスタムObjectMarshaller
in place, the output is now:ObjectMarshaller
を登録すると、出力は次のようになります:<book>The Stand</book>
h4. Customizing the Name of the Root Element
ルート要素の名前をカスタマイズする
If you wish the customize the name of the surrounding element, you can implement NameAwareMarshaller:
もし、囲う要素の名前をカスタマイズしたい場合はNameAwareMarshallerを実装します。class BookMarshaller implements ObjectMarshaller<XML>,NameAwareMarshaller { ... String getElementName(Object o) { return 'custom-book' }}
With the above change the output would now be:
上記の変更によって、出力は以下のように変わります:<custom-book>The Stand</custom-book>
h4. Outputing Markup Using the Converters API or Builder
コンバータAPIまたはビルダーを使ってマークアップを出力する
With the passed Converter object you can explicitly code to the Converters API to stream markup to the response:
渡されたコンバータオブジェクトのコンバータAPIを通じて、レスポンスへマークアップを出力するコードをわかりやすくコーディングできます:public void marshalObject(Object object, XML converter) { Book book = (Book)object converter.attribute 'id', book.id.toString() converter.attribute 'date-released', book.dateReleased.toString() converter.startNode 'title' converter.chars book.title converter.end()}
The above code results in:
上記コードの結果は次のようになります:<book id="1" date-released="..."> <title>The Stand</title> </book>
You can also use a builder notation to achieve a similar result (although the builder notation does not work for
また、ビルダー記法を使って同じような結果を得ることもできます:CompileStatic
):public void marshalObject(Object object, XML converter) { Book b = (Book)object converter.build { book(id: b.id) { title b.title } } }
h4. Using the convertAnother Method to Recursively Convert Objects
再帰的にオブジェクトをコンバートするためにconvertAnotherメソッドを使う
To create more complex responses you can use the
より複雑なレスポンスを生成するには、関連など他のオブジェクトをコンバートするconvertAnother
method to convert associations and other objects:convertAnother
メソッドを使います:public void marshalObject(Object object, XML converter) { Book book = (Book)object converter.startNode 'title' converter.chars book.title converter.end() if (book.authors) { converter.startNode 'authors' for(author in book.authors) { converter.convertAnother author } converter.end() } }
8.1.6.5 カスタムレンダラーの実装
If you want even more control of the rendering or prefer to use your own marshalling techniques then you can implement your own
もし、さらにレンダリングをコントロールしたい、または独自のマーシャリング技術を使いたいといった場合、自身のRenderer
instance. For example below is a simple implementation that customizes the rendering of the Book
class:Renderer
インスタンスを実装できます。
例えば、以下はBook
クラスのレンダリングをカスタマイズする簡単な実装例です:package myapp import grails.rest.render.* import org.codehaus.groovy.grails.web.mime.MimeTypeclass BookXmlRenderer extends AbstractRenderer<Book> { BookXmlRenderer() { super(Book, [MimeType.XML,MimeType.TEXT_XML] as MimeType[]) } void render(Book object, RenderContext context) { context.contentType = MimeType.XML.name def xml = new groovy.xml.MarkupBuilder(context.writer) xml.book(id: object.id, title:object.title) } }
The
スーパークラスAbstractRenderer
super class has a constructor that takes the class that it renders and the MimeType
(s) that are accepted (via the ACCEPT header or file extension) for the renderer.AbstractRenderer
は、表示するクラスと、レンダラーが受け入れる(ACCEPT
ヘッダまたはファイル拡張子から取得した)MimeType
を(複数)取るコンストラクタを持っています。To configure this renderer, simply add it is a bean to
このレンダラーを設定するには、grails-app/conf/spring/resources.groovy
:grails-app/conf/spring/resources.groovy
へビーンを単に追加します:beans = { bookRenderer(myapp.BookXmlRenderer) }
The result will be that all
その結果、すべてのBook
instances will be rendered in the following format:Book
インスタンスは以下のフォーマットで表示されます:<book id="1" title="The Stand"/>
Note that if you change the rendering to a completely different format like the above, then you also need to change the binding if you plan to support POST and PUT requests. Grails will not automatically know how to bind data from a custom XML format to a domain class otherwise. See the section on "Customizing Binding of Resources" for further information.
もし、POST・PUTリクエストをサポートする予定があり、上記のように完全に異なるフォーマットへレンダリングを変えたい場合は、バインディングもあわせて変更する必要があります。 Grailsは、カスタムXMLフォーマットから他のドメインクラスへ、どうのようにデータをバインドるするかを自動的に知ることはできません。 さらなる情報は「リソースバインディングのカスタマイズ」を参照してください。
h4. Container Renderers
コンテナレンダラー
A
grails.rest.render.ContainerRenderer
is a renderer that renders responses for containers of objects (lists, maps, collections etc.). The interface is largely the same as the Renderer
interface except for the addition of the getComponentType()
method, which should return the "contained" type. For example:grails.rest.render.ContainerRenderer
は、オブジェクトのコンテナ(リスト、マップ、コレクションなど)に対してレスポンスを表示するレンダラーです。
このインタフェースは、型パラメータが付与されたコンテナの型を返すgetComponentType()
メソッドが加えられている以外はRenderer
インタフェースとほとんど同じです。
例えば:class BookListRenderer implements ContainerRenderer<List, Book> { Class<List> getTargetType() { List } Class<Book> getComponentType() { Book } MimeType[] getMimeTypes() { [ MimeType.XML] as MimeType[] } void render(List object, RenderContext context) { .... } }
8.1.6.6 GSPを使用したカスタムレンダリング
You can also customize rendering on a per action basis using Groovy Server Pages (GSP). For example given the
Groovy Server Pages(GSP)を使ってアクションごとに、レンダリングをカスタマイズすることもできます:show
action mentioned previously:def show(Book book) { respond book }
You could supply a
そして、XMLのレンダリングをカスタマイズするためのshow.xml.gsp
file to customize the rendering of the XML:show.xml.gsp
ファイルを用意します:<%@page contentType="application/xml"%> <book id="${book.id}" title="${book.title}"/>
8.1.7 アプリケーション状態エンジンとしてのハイパーメディア
HATEOS, an abbreviation for Hypermedia as the Engine of Application State, is a common pattern applied to REST architectures that uses hypermedia and linking to define the REST API.
HATEOSは、アプリケーション状態エンジンとしてのハイパーメディア(Hypermedia as the Engine of Application State)の略称で、ハイパーメディアとリンク使うRESTアーキテクチャを適用した、REST APIを定義するための共通パターンです。Hypermedia (also called Mime or Media Types) are used to describe the state of a REST resource, and links tell clients how to transition to the next state. The format of the response is typically JSON or XML, although standard formats such as Atom and/or HAL are frequently used.
ハイパーメディア(MIMEタイプ、またはメディアタイプとも呼ばれる)は、RESTリソースの状態を記述するために使われます。
そして、リンクはどのように次の状態へ遷移すればよいかをクライアントに伝えます。
レスポンスのフォーマットは主にJSONやXMLが使われますが、その中でもAtomやHALのような標準的なフォーマットがよく使われます。
8.1.7.1 HALサポート
HAL is a standard exchange format commonly used when developing REST APIs that follow HATEOAS principals. An example HAL document representing a list of orders can be seen below:
HALは、HATEOASに沿ったREST APIを開発する際の標準な交換フォーマットとして知られています。
例えば、注文の一覧をHALを使って表現すると、以下のようになります:{ "_links": { "self": { "href": "/orders" }, "next": { "href": "/orders?page=2" }, "find": { "href": "/orders{?id}", "templated": true }, "admin": [{ "href": "/admins/2", "title": "Fred" }, { "href": "/admins/5", "title": "Kate" }] }, "currentlyProcessing": 14, "shippedToday": 20, "_embedded": { "order": [{ "_links": { "self": { "href": "/orders/123" }, "basket": { "href": "/baskets/98712" }, "customer": { "href": "/customers/7809" } }, "total": 30.00, "currency": "USD", "status": "shipped" }, { "_links": { "self": { "href": "/orders/124" }, "basket": { "href": "/baskets/97213" }, "customer": { "href": "/customers/12369" } }, "total": 20.00, "currency": "USD", "status": "processing" }] } }
h4. Exposing Resources Using HAL
HALを使ったリソースの公開
To return HAL instead of regular JSON for a resource you can simply override the renderer in
リソースに対して、いつものJSONの代わりにHALを返すには、grails-app/conf/spring/resources.groovy
with an instance of grails.rest.render.hal.HalJsonRenderer
(or HalXmlRenderer
for the XML variation):grails.rest.render.hal.HalJsonRenderer
(XMLの場合はHalXmlRenderer
)のインスタンスを使い、grails-app/conf/spring/resources.groovy
の中でレンダラーを単に上書きします:import grails.rest.render.hal.* beans = { halBookRenderer(HalJsonRenderer, rest.test.Book) }
With the bean in place requesting the HAL content type will return HAL:
コンテンツタイプがHALのリクエストでは、このビーンがHALを返します:$ curl -i -H "Accept: application/hal+json" http://localhost:8080/myapp/books/1HTTP/1.1 200 OK Server: Apache-Coyote/1.1 Content-Type: application/hal+json;charset=ISO-8859-1{ "_links": { "self": { "href": "http://localhost:8080/myapp/books/1", "hreflang": "en", "type": "application/hal+json" } }, "title": ""The Stand"" }
To use HAL XML format simply change the renderer:
XMLフォーマットのHALを使うには単にレンダラーを変更します:import grails.rest.render.hal.* beans = { halBookRenderer(HalXmlRenderer, rest.test.Book) }
h4. Using Custom Media / Mime Types
カスタムMIME(メディア)タイプの使用
If you wish to use a custom Mime Type then you first need to declare the Mime Types in
もし、カスタムMIMEタイプを使いたい場合は、はじめにgrails-app/conf/Config.groovy
:grails-app/conf/Config.groovy
の中でMIMEタイプを宣言する必要があります:grails.mime.types = [ all: "*/*", book: "application/vnd.books.org.book+json", bookList: "application/vnd.books.org.booklist+json", … ]
It is critical that place your new mime types after the 'all' Mime Type because if the Content Type of the request cannot be established then the first entry in the map is used for the response. If you have your new Mime Type at the top then Grails will always try and send back your new Mime Type if the requested Mime Type cannot be established.
'all'のMIMEタイプの後に、新たなMIMEタイプを宣言しているのには重要な意味があります。 なぜなら、リクエストのコンテンツタイプが不明な場合は、マップ内の最初のエントリがレスポンスのコンテンツタイプとして使われるためです。 もし、新たなMIMEタイプが先頭にあるとすると、リクエストのコンテンツタイプが不明な場合に、Grailsは新たなMIMEタイプを常に送り返すことになります。
Then override the renderer to return HAL using the custom Mime Types:
そして、カスタムMIMEタイプを使ってHALを返すためにレンダラーを上書きします:import grails.rest.render.hal.* import org.codehaus.groovy.grails.web.mime.*beans = { halBookRenderer(HalJsonRenderer, rest.test.Book, new MimeType("application/vnd.books.org.book+json", [v:"1.0"])) halBookListRenderer(HalJsonCollectionRenderer, rest.test.Book, new MimeType("application/vnd.books.org.booklist+json", [v:"1.0"])) }
In the above example the first bean defines a HAL renderer for a single book instance that returns a Mime Type of
上記の例で、初めのビーンは単一のbookインスタンスに対して、application/vnd.books.org.book+json
. The second bean defines the Mime Type used to render a collection of books (in this case application/vnd.books.org.booklist+json
).application/vnd.books.org.book+json
のMIMEタイプを返すHALのレンダラーを定義しています。
2つ目のビーンはbookのコレクションを表示するために使われるMIMEタイプを定義しています(この場合はapplication/vnd.books.org.booklist+json
)。With this in place issuing a request for the new Mime Type returns the necessary HAL:
この新たなMIMEタイプに対してリクエストが投げられると必要なHALを返します:$ curl -i -H "Accept: application/vnd.books.org.book+json" http://localhost:8080/myapp/books/1HTTP/1.1 200 OK Server: Apache-Coyote/1.1 Content-Type: application/vnd.books.org.book+json;charset=ISO-8859-1 { "_links": { "self": { "href": "http://localhost:8080/myapp/books/1", "hreflang": "en", "type": "application/vnd.books.org.book+json" } }, "title": ""The Stand"" }
h4. Customizing Link Rendering
リンクレンダリングのカスタマイズ
An important aspect of HATEOAS is the usage of links that describe the transitions the client can use to interact with the REST API. By default the
HATEOASの重要な側面は、遷移を表現したリンクの使い方です。
クライアントはREST APIでのやり取りにこれを使えます。
デフォルトでHalJsonRenderer
will automatically create links for you for associations and to the resource itself (using the "self" relationship).HalJsonRenderer
は、関連と("self"関連を使って)自身のリソースへのリンクを自動的に作成します。
However you can customize link rendering using the
リンクのレンダリングを、link
method that is added to all domain classes annotated with grails.rest.Resource
or any class annotated with grails.rest.Linkable
. For example, the show
action can be modified as follows to provide a new link in the resulting output:
grails.rest.Resource
が付与されたドメインクラス、またはgrails.rest.Linkable
が付与されたクラスに追加されたlink
メソッドを使って、カスタマイズすることもできます。
例えば、show
アクションで結果を出力する際に新たなリンクを追加するには、以下のように変更します:def show(Book book) {
book.link rel:'publisher', href: g.link(resource:"publisher", params:[bookId: book.id])
respond book
}
Which will result in output such as:
この出力結果は次のようになります:{ "_links": { "self": { "href": "http://localhost:8080/myapp/books/1", "hreflang": "en", "type": "application/vnd.books.org.book+json" } "publisher": { "href": "http://localhost:8080/myapp/books/1/publisher", "hreflang": "en" } }, "title": ""The Stand"" }
The
このlink
method can be passed named arguments that match the properties of the grails.rest.Link
class.link
メソッドはgrails.rest.Link
クラスのプロパティに一致する名前付き引数を渡せます。
8.1.7.2 Atomサポート
Atom is another standard interchange format used to implement REST APIs. An example of Atom output can be seen below:
AtomはREST APIを実装するために使われる、もう1つの標準的な変換フォーマットです。
Atomの出力例は次のようになります:<?xml version="1.0" encoding="utf-8"?> <feed xmlns="http://www.w3.org/2005/Atom"> <title>Example Feed</title> <link href="http://example.org/"/> <updated>2003-12-13T18:30:02Z</updated> <author> <name>John Doe</name> </author> <id>urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6</id> <entry> <title>Atom-Powered Robots Run Amok</title> <link href="http://example.org/2003/12/13/atom03"/> <id>urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a</id> <updated>2003-12-13T18:30:02Z</updated> <summary>Some text.</summary> </entry></feed>
To use Atom rendering again simply define a custom renderer:
Atomのレンダリングを使うには、これまでと同じようにカスタムレンダラーを登録します:import grails.rest.render.atom.* beans = { halBookRenderer(AtomRenderer, rest.test.Book) halBookListRenderer(AtomCollectionRenderer, rest.test.Book) }
8.1.7.3 Vnd.Errorサポート
Vnd.Error is a standardised way of expressing an error response.
Vnd.Errorはエラーレスポンスの表現方法を標準化したものです。By default when a validation error occurs when attempting to POST new resources then the errors object will be sent back allow with a 422 respond code:
デフォルトでは、新しいリソースをPOSTする際にバリデーションエラーが発生した場合、エラーオブジェクトが422のレスポンスコードで送り返されます:$ curl -i -H "Accept: application/json" -H "Content-Type: application/json" -X POST -d "" http://localhost:8080/myapp/booksHTTP/1.1 422 Unprocessable Entity Server: Apache-Coyote/1.1 Content-Type: application/json;charset=ISO-8859-1{"errors":[{"object":"rest.test.Book","field":"title","rejected-value":null,"message":"Property [title] of class [class rest.test.Book] cannot be null"}]}
If you wish to change the format to Vnd.Error then simply register
もしフォーマットをVnd.Errorへ変更したい場合は、grails.rest.render.errors.VndErrorJsonRenderer
bean in grails-app/conf/spring/resources.groovy
:grails-app/conf/spring/resources.groovy
の中にgrails.rest.render.errors.VndErrorJsonRenderer
ビーンを単に登録します:
beans = { vndJsonErrorRenderer(grails.rest.render.errors.VndErrorJsonRenderer) // for Vnd.Error XML format vndXmlErrorRenderer(grails.rest.render.errors.VndErrorXmlRenderer) }
Then if you alter the client request to accept Vnd.Error you get an appropriate response:
そして、Vnd.Errorを受け入れるようにクライアントのリクエストを変更すると、次のようにレスポンスを得られます:$ curl -i -H "Accept: application/vnd.error+json,application/json" -H "Content-Type: application/json" -X POST -d "" http://localhost:8080/myapp/books HTTP/1.1 200 OK Server: Apache-Coyote/1.1 Content-Type: application/vnd.error+json;charset=ISO-8859-1[ { "logref": ""book.nullable"", "message": "Property [title] of class [class rest.test.Book] cannot be null", "_links": { "resource": { "href": "http://localhost:8080/rest-test/books" } } } ]
8.1.8 リソースバインディングのカスタマイズ
The framework provides a sophisticated but simple mechanism for binding REST requests to domain objects and command objects. One way to take advantage of this is to bind the
フレームワークは高機能ですが、ドメインオブジェクトとコマンドオブジェクトへのRESTリクエストのバインディングに関しては、シンプルな作りになっています。
効果的なやり方の1つは、コントローラの中でrequest
property in a controller the properties
of a domain class. Given the following XML as the body of the request, the createBook
action will create a new Book
and assign "The Stand" to the title
property and "Stephen King" to the authorName
property.request
プロパティをドメインクラスのプロパティへバインドすることです。
リクエストのボディとして以下のようなXMLを与えた場合、createBook
アクションは新しいBook
を作成し、"The Stand"をtitle
プロパティへ、"Stephen King"をauthorName
プロパティへ代入します。<?xml version="1.0" encoding="UTF-8"?> <book> <title>The Stand</title> <authorName>Stephen King</authorName> </book>
class BookController { def createBook() {
def book = new Book()
book.properties = request // …
}
}
If the root element of the XML document contains an
もしXMLドキュメントのルート要素がid
attribute, the id
value will be used to retrieve the corresponding persistent instance from the database and then the rest of the document will be bound to the instance. If no corresponding record is found in the database, the command object reference will be null.id
属性を含む場合は、そのid
値はデータベースから対応する永続化インスタンスを検索するために使われます。
そして、ドキュメントの残りはインスタンスへバインドされます。
もし、一致するレコードがデータベース内に見つからない場合は、コマンドオブジェクトの参照はnullになります。<?xml version="1.0" encoding="UTF-8"?> <book> <title>The Stand</title> <authorName>Stephen King</authorName> </book>
Command objects will automatically be bound with the body of the request:
コマンドオブジェクトにはリクエストのボディが自動的にバインドされます。class BookController { def createBook(BookCommand book) { // … } }class BookCommand { String title String authorName }
If the command object type is a domain class and the root element of the XML document contains an
もし、コマンドオブジェクトの型がドメインクラスで、かつXMLドキュメントのルート要素がid
attribute, the id
value will be used to retrieve the corresponding persistent instance from the database and then the rest of the document will be bound to the instance. If no corresponding record is found in the database, the command object reference will be null.id
属性を含んでいる場合は、そのid
値はデータベースから対応する永続化インスタンスを検索するために使われます。
そして、ドキュメントの残りはインスタンスへバインドされます。
もし、一致するレコードがデータベース内に見つからない場合は、コマンドオブジェクトの参照はnullになります。<?xml version="1.0" encoding="UTF-8"?> <book id="42"> <title>Walden</title> <authorName>Henry David Thoreau</authorName> </book>
class BookController { def updateBook(Book book) { // The book will have been retrieved from the database and updated // by doing something like this: // // book == Book.get('42') // if(book != null) { // book.properties = request // } // // the code above represents what the framework will // have done. There is no need to write that code. // ... } }
The data binding depends on an instance of the DataBindingSource interface created by an
instance of the DataBindingSourceCreator interface. The specific
implementation of
データバインディングはDataBindingSourceCreatorインタフェースのインスタンスによって作られるDataBindingSourceインタフェースのインスタンスに依存しています。
DataBindingSourceCreator
will be selected based on the contentType
of the request. Several implementations
are provided to handle common content types. The default implementations will be fine for most use cases. The following table
lists the content types which are supported by the core framework and which DataBindingSourceCreator
implementations are used for each.DataBindingSourceCreator
の具体的な実装はリクエストのcontentType
を元に選択されます。
主要なコンテンツタイプをハンドリングするためにいくつかの実装が提供されています。
大抵の場合にこのデフォルト実装で十分なはずです。
以下のテーブルのコンテンツタイプ一覧がコアフレームワークによってサポートされており、それぞれ対応するDataBindingSourceCreator
の実装が使われます。Content Type(s) | Bean Name | DataBindingSourceCreator Impl. |
---|---|---|
application/xml, text/xml | xmlDataBindingSourceCreator | org.codehaus.groovy.grails.web.binding.bindingsource.XmlDataBindingSourceCreator |
application/json, text/json | jsonDataBindingSourceCreator | org.codehaus.groovy.grails.web.binding.bindingsource.JsonDataBindingSourceCreator |
application/hal+json | halJsonDataBindingSourceCreator | org.codehaus.groovy.grails.web.binding.bindingsource.HalJsonDataBindingSourceCreator |
application/hal+xml | halXmlDataBindingSourceCreator | org.codehaus.groovy.grails.web.binding.bindingsource.HalXmlDataBindingSourceCreator |
コンテンツタイプ | ビーン名 | DataBindingSourceCreator実装 |
---|---|---|
application/xml, text/xml | xmlDataBindingSourceCreator | org.codehaus.groovy.grails.web.binding.bindingsource.XmlDataBindingSourceCreator |
application/json, text/json | jsonDataBindingSourceCreator | org.codehaus.groovy.grails.web.binding.bindingsource.JsonDataBindingSourceCreator |
application/hal+json | halJsonDataBindingSourceCreator | org.codehaus.groovy.grails.web.binding.bindingsource.HalJsonDataBindingSourceCreator |
application/hal+xml | halXmlDataBindingSourceCreator | org.codehaus.groovy.grails.web.binding.bindingsource.HalXmlDataBindingSourceCreator |
In order provide your own
これらのコンテンツタイプに対して、独自のDataBindingSourceCreator
for any of those content types, write a class which implements
DataBindingSourceCreator
and register an instance of that class in the Spring application context. If you
are replacing one of the existing helpers, use the corresponding bean name from above. If you are providing a
helper for a content type other than those accounted for by the core framework, the bean name may be anything that
you like but you should take care not to conflict with one of the bean names above.DataBindingSourceCreator
を提供するには、DataBindingSourceCreator
の実装クラスを書き、Springのアプリケーションコンテキスト内にそのクラスのインスタンスを登録します。
既存のヘルパーを置き換える場合は、上記のビーン名に対応する名前を使います。
もし、これらコアフレームによって提供される以外のコンテンツタイプのヘルパーを提供する場合、ビーンの名前は何でも好きに設定可能ですが、上記のビーン名と競合しないように注意してください。The
DataBindingSourceCreator
interface defines just 2 methods:DataBindingSourceCreator
インタフェースは2つのメソッドを定義しています:package org.grails.databinding.bindingsourceimport org.codehaus.groovy.grails.web.mime.MimeType
import org.grails.databinding.DataBindingSource/**
* A factory for DataBindingSource instances
*
* @since 2.3
* @see DataBindingSourceRegistry
* @see DataBindingSource
*
*/
interface DataBindingSourceCreator { /**
* return All of the {
link MimeType} supported by this helper
*/
MimeType[] getMimeTypes() /**
* Creates a DataBindingSource suitable for binding bindingSource to bindingTarget
*
* @param mimeType a mime type
* @param bindingTarget the target of the data binding
* @param bindingSource the value being bound
* @return a DataBindingSource
*/
DataBindingSource createDataBindingSource(MimeType mimeType, Object bindingTarget, Object bindingSource)
}
AbstractRequestBodyDataBindingSourceCreator
is an abstract class designed to be extended to simplify writing custom
AbstractRequestBodyDataBindingSourceCreatorはこのクラスを拡張することで、簡単に独自のDataBindingSourceCreator
classes. Classes which
extend AbstractRequestbodyDatabindingSourceCreator
need to implement a method named createBindingSource
which accepts an InputStream
as an argument and returns a DataBindingSource
as well as implementing the getMimeTypes
method described in the DataBindingSourceCreator
interface above. The InputStream
argument to createBindingSource
provides access to the body of the request.DataBindingSourceCreator
クラスを書けるよう設計された抽象クラスです。
AbstractRequestbodyDatabindingSourceCreator
を継承したクラスは、引数としてInputStream
を受け取りDataBindingSource
を返す、createBindingSource
という名前のメソッドを実装する必要があります。
また、上記のDataBindingSourceCreator
インタフェース内で宣言されたgetMimeTypes
メソッドの実装が必要です。
createBindingSource
へのInputStream
の引数は、リクエストボディへのアクセスを提供します。The code below shows a simple implementation.
次のコードに簡単な実装例を示します。
// src/groovy/com/demo/myapp/databinding/MyCustomDataBindingSourceCreator.groovy package com.demo.myapp.databindingimport org.codehaus.groovy.grails.web.mime.MimeType import org.grails.databinding.DataBindingSource import org.grails.databinding.SimpleMapDataBindingSource import org.grails.databinding.bindingsource.AbstractRequestBodyDataBindingSourceCreator/** * A custom DataBindingSourceCreator capable of parsing key value pairs out of * a request body containing a comma separated list of key:value pairs like: * * name:Herman,age:99,town:STL * */ class MyCustomDataBindingSourceCreator extends AbstractRequestBodyDataBindingSourceCreator { @Override public MimeType[] getMimeTypes() { [new MimeType('text/custom+demo+csv')] as MimeType[] } @Override protected DataBindingSource createBindingSource(InputStream inputStream) { def map = [:] def reader = new InputStreamReader(inputStream) // this is an obviously naive parser and is intended // for demonstration purposes only. reader.eachLine { line -> def keyValuePairs = line.split(',') keyValuePairs.each { keyValuePair -> if(keyValuePair?.trim()) { def keyValuePieces = keyValuePair.split(':') def key = keyValuePieces[0].trim() def value = keyValuePieces[1].trim() map[key] = value } } } // create and return a DataBindingSource which contains the parsed data new SimpleMapDataBindingSource(map) } }
An instance of
MyCustomDataSourceCreator
needs to be registered in the spring application context.MyCustomDataSourceCreator
のインスタンスはSpringのアプリケーションコンテキスト内に登録が必要です。// grails-app/conf/spring/resources.groovy beans = { myCustomCreator com.demo.myapp.databinding.MyCustomDataBindingSourceCreator // … }
With that in place the framework will use the
上記のようにすることで、フレームワークは"text/custom+demo+csv"のmyCustomCreator
bean any time a DataBindingSourceCreator
is needed
to deal with a request which has a contentType
of "text/custom+demo+csv".contentType
を持ったリクエストを処理するDataBindingSourceCreator
が必要な場合に、myCustomCreator
ビーンを使います。
8.2 SOAP
Grails does not feature SOAP support out-of-the-box, but there are several plugins that can help for both producing SOAP servers and calling SOAP web services.SOAP Clients
To call SOAP web services there are generally 2 approaches taken, one is to use a tool to generate client stubs, the other is to manually construct the SOAP calls. The former can be easier to use, but the latter provides more flexibility / control.The CXF client plugin uses the CXF framework, which includes awsdl2java
tool for generating a client. There is nothing Groovy/Grails specific here in the generated code as it simply provides a Java API which you can invoke to call SOAP web services.See the documentation on the CXF client plugin for further information.Alternatively, if you prefer more control over your SOAP calls the WS-Lite library is an excellent choice and features a Grails plugin. You have more control over the SOAP requests sent, and since Groovy has fantastic support for building and parsing XML it can be very productive approach.Below is an example of a SOAP call with wslite:withSoap(serviceURL: 'http://www.holidaywebservice.com/Holidays/US/Dates/USHolidayDates.asmx') { def response = send { body { GetMothersDay(xmlns: 'http://www.27seconds.com/Holidays/US/Dates/') { year(2011) } } } println response.GetMothersDayResponse.GetMothersDayResult.text() }
SOAP Servers
Again, Grails does not have direct support for exposing SOAP web services, however if you wish to expose a SOAP service from your application then the CXF plugin (not to be confused with the cxf-client plugin), provides an easy way to do so.Typically it involves taking a Grails service and adding 'expose'-style configuration, such as the below:static expose = EndpointType.JAX_WS_WSDL //your path (preferred) or url to wsdl static wsdl = 'org/grails/cxf/test/soap/CustomerService.wsdl'
8.3 RSSとAtom
No direct support is provided for RSS or Atom within Grails. You could construct RSS or ATOM feeds with the render method's XML capability. There is however a Feeds plugin available for Grails that provides a RSS and Atom builder using the popular ROME library. An example of its usage can be seen below:
RSSやAtomについては、Grailsへの直接的な機能は提供されていません。renderメソッドのXML機能を使用して、RSSやATOMのフィードを構築することができます。他に、ROME ライブラリを使用してRSSとAtomビルダを提供したFeedsプラグイン もあります。その使用例を以下に示します。def feed() { render(feedType: "rss", feedVersion: "2.0") { title = "My test feed" link = "http://your.test.server/yourController/feed" for (article in Article.list()) { entry(article.title) { link = "http://your.test.server/article/${article.id}" article.content // return the content } } } }