はじめまして。モバイル事業部の九岡です。
昨年9月にフリューに転職してきて、社会人としては今年で4年目になります。
JavaScriptとScala、そして御坂美琴(重要)をこよなく愛するWebエンジニアです。

本ブログでは、私が個人的に気になっている技術(主にプログラミング言語やWebフレームワークになると思います)を「○○を作ってみた」という形でご紹介していきます。

早速ですが、今回はPlay frameworkでRESTfulなWebサービスを作ってみました。

Play frameworkって?

Play! frameworkは、2009年頃から開発が続けられている、個人的にイチオシのJava・Scala向けWebフレームワークです。
発表当時は「Ruby on Rails for Java」のような取り上げられ方をされて話題になったのですが、何故か流行らなかった不遇のWebフレームワークです。
つい先日(2011/4/13)バージョン1.2がリリースされるなど、現在でも活発に開発が続いています。

RESTfulって?

RESTfulは簡単にいうと、「リソースごとにユニークなURIを振り、リソースに対する操作をHTTP METHODで区別する」ような構造のWebサービスのことです。

参考:REST – Wikipedia

詳細な説明は、実装例とあわせてします。

つくってみる

今回は、簡易な「画像共有サービス」をつくってみました。
巷の画像共有サービス(Flickrのようなもの)にはアカウント登録やコメント機能など色々なおまけがついていますが、ここでは画像を

  • アップロードできる
  • 検索できる
  • 閲覧できる
  • 変更できる
  • 削除できる

という5機能に絞ります。

これをREST的に解釈して、今回は以下の2つのリソースがあるということにします。

リソース URI
画像 /photos
/photos/{id}
画像ファイル /photos/{id}/data

そして、これらのリソースに対する操作をそれぞれAPIとして切り出します。
5つの機能をそれぞれ「リソースとそれに対する操作(HTTP METHOD)」に割り当てて、今回は以下のように決めました。

API(METHOD + URI) 機能
POST /photos アップロード
GET /photos 検索
GET /photos/{id}/data 閲覧(アップロードされた画像ファイルそのもの)
UPDATE /photos/{id} 画像のメタデータ(今回はタイトルのみ)を変更
DELETE /photos/{id} 画像を削除する

API一覧

これを早速Playで実装してみます。

ひな形の作成

まず、playでアプリの雛形を作成します。
どんなアプリを実装するときも、この方法で作成した雛形を少しずつ変更していけばOKです。

これでカレントディレクトリのrestというディレクトリ以下にアプリが作成されました。

routes追加

conf/routesに前述の5つのAPIのルート定義を書きます。

書式は「HTTP METHOD + パス + コントローラのクラス名.メソッド名」になっています。
指定したパスに、指定したメソッドでリクエストが来た場合、指定したコントローラのメソッドに処理を委譲する、という意味です。
直感的ですね!

コントローラの実装

routesに記載したメソッドをコントローラに実装します。

app/controllers/Photos.java

レスポンスまわりのコードはPlay独特かもしれません。
ポイントは以下の3つです。
これ以外は見た目通りではないでしょうか。

renderJSON(photos)

で、Photoのリストをjsonにシリアライズしてレスポンスに出力します。
簡単にいうと、出力したいオブジェクトをrenderJSONするだけで参照用のWeb APIが実装できます。

render(photos)

で、PhotoのリストをHTMLテンプレートに渡した上で描画します。
renderの引数に渡したphotosは、HTMLテンプレートからもphotosという名前で参照できます。

routesで以下のようなAPIを定義したことを覚えていますか?

ここでは、/photosのパスに

  • 拡張子なし
  • json
  • xml

の3パターンを許容しています。

render()を読んだ場合、リクエストされたときの拡張子に応じて描画するテンプレートを規約により勝手に決めてくれます。
例えば、Photos.indexの場合、拡張子を省略するとindex.html、.xmlならindex.xmlというテンプレートが使われます。

notFound

404 NOT FOUNDを返します。

モデルの実装

app/models/Photo.java

ここでも詳細は割愛しますが、ポイントは以下の3点です。

getter/setterが不要

Javaに詳しい方は違和感を感じるかもしれません。
Playのモデルではgetter/setterを書かなくてもOKです。
値のget/setしかしない上にサブクラスでオーバーライドすることもないのであればgetter/setterは要らないよね、というPlay開発者の思い切った判断ですね!

インターセプター(PrePersist, PreUpdate, etc)

Playは標準でいくつかのインターセプターを用意しています。
上記の例では、モデルをDBに永続化する前に必ず呼ばれるメソッドonSave()を実現するために使っています。

ビューの作成

app/views/Photos/index.html

ここでの実装ポイントは4点です。

タグ

#{名前}#{/名前}でPlayに組み込まれているタグを呼び出せます。

リンク

@{コントローラ.メソッド(引数)}は、指定したコントローラのメソッドに対応するURLに置き換わります。

変数の利用

${変数}で、コントローラのrender()に渡した変数の値が参照できます。

POSTパラメータの指定

先ほどコントローラで

のようなメソッドを定義したことを覚えていますか?

POSTするときに、photo.title、photo.dataというパラメータを指定すると、それぞれphotoにセットされた状態でコントローラのメソッドに渡されます。

app/views/Photos/index.xml

前述の通り、/index.xmlをリクエストした場合はこのテンプレートが使われます。

実行する

で、Playに組み込みHTTPサーバが起動します。

http://localhost:9000/photos
にアクセスすると、Photos/index.htmlの内容が表示され、ページ内のフォームから画像のアップロードができます。

そして本題のRESTfulなAPIを使ってみます。
RESTfulなWebサービスの利点として、HTTP通信ができて、JSONやXMLをデコードできる言語・環境ならどこからでもアクセスできますが、ここではお手軽にjQueryでアクセスします。

画像の更新であれば、

というようなコードになります。
routesで定義したとおり、APIはHTTP METHODとパスにより区別されます。
更新APIはroutesでPUT /photos/{id}と定義したので、jQueryからリクエストするときのURLも’/photos/1’のようになります。

また、削除であれば

検索であれば

という感じになります。

総括

  • Play frameworkでRESTfulなWebサービスを実装しました
  • Play frameworkのroutes, コントローラ、モデル、ビューの基本的な書き方を説明しました
  • ブラウザからWebページとして、HTTP経由でAPIとしてアクセスできることを示しました

駆け足の説明でしたが、Play frameworkを使うとお手軽にRESTfulなWebサービスが実装できることが、雰囲気だけでもわかっていただけたでしょうか?

ソースコードはGitHubで公開しています。
試しに動かしてみたいという方はぜひgit cloneしてみてください。

次回は、このアプリのユニットテスト・ファンクションテストについて書きます。
テストのないコードはレガシーコードだ!!(挨拶)