(クイックリファレンス)

13 Webサービス

Authors: Graeme Rocher, Peter Ledbrook, Marc Palmer, Jeff Brown, Luke Daley, Burt Beckwith ,
Japanese Translation: T.Yamamoto, Japanese Grails Doc Translating Team
このドキュメントの内容はスナップショットバージョンを元に意訳されているため、一部現行バージョンでは未対応の機能もあります。

Version: 2.1.0.BUILD-SNAPSHOT

13 Webサービス

WebサービスはWebアプリケーションにWeb APIを提供するものであり、通常はSOAPまたはRESTで実装されています。

13.1 REST

RESTは本来SOAPのようなプロトコルではなく、アーキテクチャスタイルです。RESTは非常に単純です。簡素なXMLやJSONを通信媒体に用いて、基本的なシステムとGET、PUT、POSTおよびDELETEといったHTTPメソッドを「表現する」URLパターンを組み合わせています。

それぞれのHTTPメソッドは異なるアクションにマップします。例えば、GETはデータ検索、PUTはデータ作成、POSTは更新などとマップします。このため、RESTはCRUDと非常によく適合します。

URLパターン

GrailsでRESTを実装する1つ目のやり方として、以下のようにRESTfulなURLマッピングを提供する方法があります。

static mappings = {
   "/product/$id?"(resource:"product")
}

この例では、/productというURIをProductControllerに紐付けます。GET、PUT、POSTおよびDELETEといった各HTTPメソッドは、下表に示したようにコントローラ内の一意のアクションに紐付きます。

メソッドアクション
GETshow
PUTupdate
POSTsave
DELETEdelete

さらにGrailsではXMLまたJSONでの自動マーシャリングも提供しています。

HTTPメソッドをマップする仕組みを使用して、URLマッピングをHTTPメソッドにハンドルすることが可能です:

"/product/$id"(controller: "product") {
    action = [GET: "show", PUT: "update", DELETE: "delete", POST: "save"]
}

ただし、この場合は、先のresourceと違い、parseRequest変数をURLマッピングに指定しないと、XMLやJSONのマーシャリングを自動的には行いません:

"/product/$id"(controller: "product", parseRequest: true) {
    action = [GET: "show", PUT: "update", DELETE: "delete", POST: "save"]
}

HTTPメソッド

前のセクションでは、特定のHTTPメソッドを特定のコントローラのアクションにマップするURLマッピングが、どれほど簡単に定義できるかを見ました。特定のHTTPメソッドを送信するRESTクライアントを作成するのも簡単です。(GroovyのHTTPBuilderモジュールの例):

import groovyx.net.http.*
import static groovyx.net.http.ContentType.JSON

def http = new HTTPBuilder("http://localhost:8080/amazon")

http.request(Method.GET, JSON) { url.path = '/book/list' response.success = { resp, json -> for (book in json.books) { println book.title } } }

通常のブラウザからGETまたはPOST以外のメソッドの要求を発行するためには、Grailsの援助が必要となります。フォーム(form)を定義する時に、DELETEのような別のメソッドを指定できます。

<g:form controller="book" method="DELETE">
    ..
</g:form>

Grailsは_methodというhiddenパラメータを送信し、これをHTTPメソッドの要求として使用します。ブラウザ以外のクライアントでメソッドを変更するためには、X-HTTP-Method-Overrideを使用して代替メソッド名を指定します。

XMLマーシャリング - 読み取り

GrailsのXMLマーシャリングの機能をコントローラで使用して、GETメソッドの実装できます。

import grails.converters.XML

class ProductController { def show() { if (params.id && Product.exists(params.id)) { def p = Product.findByName(params.id) render p as XML } else { def all = Product.list() render all as XML } } .. }

この例では、検索した内容が存在した場合はそのProductを返し、無い場合は全てを返します。この方法で、/productにアクセスすると全リストが返り、/product/MacBook等指定して存在した場合は、そのProductを返します。

XMLマーシャリング - 更新

PUTおよびPOSTのような更新をサポートするには、Grailsのparamsオブジェクトを利用してXMLを読み取ることができます。次のようなXMLを受信したとします。

<?xml version="1.0" encoding="ISO-8859-1"?>
<product>
    <name>MacBook</name>
    <vendor id="12">
        <name>Apple</name>
     </vender>
</product>

このXMLを読み込むには、データバインディングの章に記述された内容と同じく、paramsオブジェクトから取得することができます:

def save() {
    def p = new Product(params.product)

if (p.save()) { render p as XML } else { render p.errors } }

この例では、paramsオブジェクトからproductキーを使用して取得し、Productクラスのコンストラクタに渡すことで自動的にバインドします:

def p = new Product(params.product)

興味深い点は、JSONリクエストまたはXMLリクエストの対応は、通常のフォーム送信の対応とそれほど変わりがないということです。

異なったクライアント(REST、HTMLなど)に対する異なった応答が必要な場合は、コンテントネゴシエーションを使用することができます。

Productオブジェクトが保存され、XMLとして描写されます。問題が起きた場合は、Grailsのバリデーション機能によってエラーメッセージが返信されます。

<error>
   <message>The property 'title' of class 'Person' must be specified</message>
</error>

JAX-RSでのREST

RESTful Webサービス用のJava APIをベースとしたRESTを、JAX-RSプラグイン構築することも可能です。 (JSR 311: JAX-RS)

13.2 SOAP

GrailsにはSOAPのサポートを追加するプラグインがいくつかあります。コントラクトファーストのSOAP向けにはSpring WSプラグインがあり、GrailsサービスからSOAP APIを生成したいのであれば以下のようなプラグインがあります。

ほとんどのSOAP系プラグインはGrailsのサービスexposeプロパティを定義することによって、SOAPを統合することが可能です。下記はCXFプラグインの例です:

class BookService {

static expose = ['cxf']

Book[] getBooks() { Book.list() as Book[] } }

WSDLには次のようなロケーションでアクセスすることができます。 http://127.0.0.1:8080/your_grails_app/services/book?wsdl

CXFプラグインについてもっと知りたい方は、ドキュメントを参照しましょう。

13.3 RSSとAtom

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 } } } }