シングルページアプリケーションを書く時にいっつも似たようなコードを書いていたので、勢い余って gem にしました。
使い方
gem 'restful_error'
って書いて、
class ApplicationController < ActionController::Base
include RestfulError::ActionController
って書いておく。
そうすると、
def show
@post = Post.find(param[:id])
end
みたいなアクションがあったとして、 GET /posts/10.json
すると
{ status_code: 404, message: "要求されたリソースが存在しません"}
みたいに json がちゃんと返ってくる。(もちろん status code は404)
他のステータスも自由に raise できる。
class PostsController < ApplicationController
before_action do
raise RestfulError[401] unless current_user
# or
raise RestfulError::Unauthorized unless current_user
end
end
自分でエラーを定義する時は、継承するか
class ::NoSession < RestfulError[404]; end
# or
class ::NoSession < RestfulError::NotFound; end
status_code メソッドを定義しておく (duck typing)
class OAuthController < ApplicationController
# all you need is status_code
class RequireTwitterLogin < StandardError
def status_code; 401; end
end
# or
class RequireTwitterLogin < StandardError
def status_code; :unauthorized; end
end
end
response のカスタマイズ
デフォルトで、 html, json, xml に応答する。
message のとこは
ja:
restful_error:
active_record/record_not_found: 要求されたリソースが存在しません
not_found: ページが存在しません
で設定可能。
active_record/record_not_found
は ActiveRecord::RecordNotFound
専用、 not_found
は 404 エラー汎用となる。
status_message というメソッドがある場合にはそれを優先する。
class RequireLogin < StandardError
def initialize(provider = 'Unknown')
@provider = provider
end
def status_code
:unauthorized
end
def status_message
I18n.t('restful_errors.require_login', provider: provider)
end
end
以下のいずれかを置いておけば、レスポンスを柔軟にカスタマイズ出来る。
views/restful_error/show.html.erb
views/restful_error/show.json.jbuilder
views/restful_error/show.xml.erb
(もちろん haml でも rabl でもそこはご自由に)
中身の話
ActiveRecord::RecordNotFound
が raise された時にステータスコードがちゃんと 404 になる仕組みが Rails にもともとあって、これは config/application.rb
とかで
config.action_dispatch.rescue_responses.merge!(
'MyGreatError' => :not_found,
としておくと、 raise MyGreatError
で 404 を返してくれる。
resutful_error は rescue_responses に値を追加しないが、既に追加されているものについては参照している。なので、 ActiveRecord::RecordNotFound
ではちゃんと 404 になるし、 ActiveRecord::RecordInvalid
ではちゃんと 422 になる。
ステータスコードのリストみたいなのどっかにないかなーと思ったら標準ライブラリの webrick の中に綺麗に定義されてた。
https://github.com/ruby/ruby/blob/trunk/lib/webrick/httpstatus.rb
これ、 Webrick の中に入っちゃってるのもったいないなーと思い、引っ張りだして使っています。
全てのステータスコードにシンボルが対応していて、数字でもシンボルでもどっちでも使えるようになっている。便利。
例外には message メソッドがあるのだけどこれは i18n に対応していないので使用しない。クラス名、 reason phrase (404 なら not_found みたいなやつ) の順に i18n のキーを検索して適用することにしている。
開発の邪魔にならないように、(config.consider_all_requests_local = true) の場合 status_code が 500 のエラーは処理せず raise している。
FAQ
どこがrestfulやねん
すいません、誇大広告です。
いちおう気持ちとしては、'RESTってhttpのURLとかメソッドとかを、ちゃんと本来の意味で使おうね' っていう運動だと理解しており、適切なステータスコードで結果を返すのもその方向性だろうと思って、この名前にしています。
ツッコミをお待ちしております