##何が問題かというと
RailsでJSON APIを定義する時、素のままでやろうとすると
コントーラでto_jsonを呼んだり、モデルにas_jsonを定義したりすることになるかと思います。
モデルに書くとAPIによって出力内容を変えたい場合にとても苦労します。
API数が増えれば増えるほどモデルが複雑になっていきます。
APIレスポンスとしてのJSONはコントローラやモデルに書くべきでしょうか?
ビューに書いた方が自然ではないでしょうか?
##RABL
とはいえ、他の画面用のビューのようにERBでJSONレスポンスを書くというのはないでしょう。
そこで、JSONのAPIレスポンスを表現することに特化したDSLライブラリのRABLが使えます。
http://nesquena.github.com/rabl/
https://github.com/nesquena/rabl
http://engineering.gomiso.com/2011/06/27/building-a-platform-api-on-rails/
##インストール
RABLをインストールするにはGemfileに以下の行を追加して、bundle installします。
gem 'rabl'
##APIコントローラ
まずはAPI専用のコントローラクラスを用意します。
class ApiController < ApplicationController
end
##RABLが不要なAPI
追加/変更/削除等のレスポンスが非常にシンプルなAPIであれば、RABLを使う必要はありません。
その場合はコントローラ+モデルだけで書くことができます。
例としてユーザ名を変更するAPI、updateUserNameを定義してみます。
class ApiController < ApplicationController
def updateUserName
current_user.name = params[:name]
if current_user.save
render json: {result: true}
else
render json: {error: {code: Error::SAVE_FAILURE}}
end
end
end
ユーザ名の変更に成功すればresult:trueを返し、失敗した場合はエラーコードを返すようになっています。
この程度であればビューを括りだす必要はないですね。
##RABLが便利なAPI
一覧取得や詳細取得などの情報表示に関わるAPIは、レスポンスが複雑になってきますので、
RABLを使ったビューを定義すると奇麗になります。
例としてアイテム一覧を取得するAPIを定義します。
class ApiController < ApplicationController
def getItems
@items = Item.all(:order => "created_at DESC")
end
end
コントローラのアクションはこれだけです!
何をレスポンスに含めるのか、どういった構造で返すのか、
そういった情報はRABLビューで定義します。
RABLビューファイルは「アクション名.json.rabl」という名前にします。
collection @items, :root => :result, :object_root => false
attribute :id, :name
node(:photo_url) do |item|
item.photo.url
end
このAPIは次のようなJSONレスポンスを返します。
{
"result": [
{
"id": 1, "name": "Taro Yamada", "photo_url": "http://.../00001"
},
{
"id": 2, "name": "Jiro Suzuki", "photo_url": "http://.../00002"
}
]
}
RABLのDSLによってJSONのレスポンスが簡潔に定義できているのがわかるかと思います。
もう少し複雑な例
もう1例としてアイテム詳細APIを定義してみます。
class ApiController < ApplicationController
def getItemDetail
@item = Item.find(params[:id])
end
end
object false
child(@item => :result) do
attribute :id, :name, :memo, :bought_at, :location_name, :price,
:user, :likes, :comments, :tags
node :photo_url do |item|
item.photo.url
end
child :user do
attribute :id, :name
node :photo_url do |user|
user.photo.url
end
end
child(:tags) do
attribute :name
end
child(:likes) do
attribute :user_id
end
child(:comments) do
attribute :user, :body, :created_at
child(:user) do
attribute :id
node :photo_url do |user|
user.photo.url
end
end
end
end
JSON出力結果は書くのが大変なので割愛しますが、
アイテムの中にコメントが複数あって、各コメントには書込んだユーザがいて、ユーザには写真があって、
といった複雑なレスポンスがわかりやすく記述できるようになります。
まとめ
RABLを使う事で情報表示系のAPIのメンテナンス性が格段に上がりました。
モバイルアプリやフルJavaScriptクライアント用にJSON APIを用意する時にはぜひ、おすすめです。
上記の例で出てくるRABL DSLの詳細については、この辺りをご参照ください。
http://nesquena.github.com/rabl/#usage
object, collection, child, attribute, node