ActiveModel::Errors クラスの to_hash のメソッドで引数を true にすることで良い感じに作ることができそうでした。あなた引数渡せるのね!
account = Account.new(email: "invalid", password: "") # 無効な値を入れる account.valid? account.errors.to_hash # 引数なし # => {:email=>["は不正な値です"], :password=>["を入力してください"]} account.errors.to_hash(true) # 引数あり # => {:email=>["メールアドレスは不正な値です"], :password=>["パスワードを入力してください"]}
これを良い感じに hash に詰めて返すだけで
render status: 422, json: { message: '不正な入力値です', errors: account.errors.to_hash(true) }
このようなフロントでもそのまま扱いやすいレスポンスを作れます。Rails 便利ぃ〜
{ "message": "不正な入力値です。", "errors": { "email": ["メールアドレスは不正な値です"], "password": ["パスワードを入力してください"] } }
create! や save! などで例外を発生させるような実装にする場合は rescue_form で共通処理を入れてもよさそうです。
class Api::ExamplesController < ApplicationController rescue_from ActiveRecord::RecordInvalid, with: :respond_with_record_invalid_exception def create Account.create! account_params # xxx end private def account_params # xxx end def respond_with_record_invalid_exception(exception) if exception.record.blank? render status: 400, json: { message: '不正なリクエストです' } end render status: 422, json: { message: '不正な入力値です', errors: exception.record.errors.to_hash(true) } end end
単純にフォームの input とマッチする key で各エラー内容を返してあげると、フロントでごにょごにょせずに使いまわせるので個人的にはこの返し方いいなと思っています。もちろんフロントの要件が複雑化したり、サーバでの共通文言以外で返す必要があるケースは多いのですが、単純な構成のレスポンスで問題ないケースでは使っていきたいと思いました。*1
さいごに
Rails は本当に便利すぎる。。こんな実装欲しいなと思った時は Ruby on Rails API を読んでみるとかなりの確率でメソッドが用意されています。たまに気になったクラスの部分を見てみるだけで、知らない便利メソッドに出会えたり、今回のようなオプション的な機能を知れたり楽しいです。他にも Rails っぽい命名なんかを調べるのにもいいかもしれません。こういう動詞使われているかな、用意されている処理の命名と意味がバッティングしてしまわないかなとかとか。。。
*1:個人開発とか...