7

jbuilder ビューにエラー メッセージを表示しようとしています。たとえば、私が持っているかもしれない1つのルートは次のとおりです。

/foos/:id/bars

ユーザーが送信したものが存在しないか無効である場合:id、それに応じてエラー メッセージをファイルに表示できるようにしたいと考えていindex.json.builderます。

Rails を使用して、これを行うための最良の方法は何ですか? コントローラーには次のようなものがあります。

def index
  @bar = Bar.where(:foo_id => params[:id])
end

この場合、 でparams[:id]あるかnil、そのオブジェクトが存在しない可能性があります。error.json.builderここで行う最善の方法は、コントローラーで処理して を明示的にレンダリングすることなのか、それともindex.json.builderビュー自体で処理することなのかわかりません。これを行う正しい方法は何ですか?それがにある場合はindex.json.builderparams[:id]そこで確認できますか? かどうかはわかります@bar.nil?が、逆はわかりませんか?

4

2 に答える 2

5

インデックスは実際にはリスト/コレクション用であるため、ショーを意味していたと思います。そして、あなたは.firstどこにでも乗るべきです、さもなければあなたは関係を持っているだけですよね?次に、エラーを発生させるために使用します。これは、Rails 4 public_exceptions.first!の Rails の Rack ミドルウェアが基本的な方法で処理するためです。

def show
  # need to do to_s on params value if affected by security issue CVE-2013-1854
  @bar = Bar.where(:foo_id => params[:id].to_s).first!
end

も使用できます@bar = Bar.find(params[:id])が、これは推奨されておらず、Rails 4.1 で削除されgem 'activerecord-deprecated_finders'ます。その後、Gemfile に追加して使用する必要があります。

インデックスについては、おそらく@bars = Bar.all. 何らかの理由でフィルタリングしたいがスコープを設定したくない場合などは、@bars = Bar.where(...).to_aまたは類似のものを使用できます。

Rails 4: ラックでの基本的な例外処理は自動です

クエリがエラーを開始する限り、Rails 4 はサポートされている形式のエラーのメッセージ部分を返すことができるはずですwhere to_(format)(例: json、xml など)。

その理由を確認するには、Rails の Rack public_exceptionsミドルウェアを見てください。

500.htmlhtml の場合、Rails の public ディレクトリから関連ファイルを読み取って、ステータス コード (サーバー エラー/HTTP 500 など) を取得しようとします。

他の形式の場合はto_(the format)、ハッシュで実行しようとします: { :status => status, :error => exception.message }. これがどのように機能するかを確認するには、Rails のコンソールに移動します。

$ rails c
...
1.9.3p392 :001 > {status: 500, error: "herro shraggy!"}.to_xml
 => "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<hash>\n  <status type=\"integer\">500</status>\n  <error>herro shraggy!</error>\n</hash>\n" 
1.9.3p392 :002 > {status: 500, error: "herro shraggy!"}.to_json
 => "{\"status\":500,\"error\":\"herro shraggy!\"}" 

ミドルウェアではX-Cascade、コード内のヘッダーと、Rack での Rails の例外処理に関連するさまざまな場所にヘッダーが表示されます。この回答 に従ってX-Cascadeヘッダーは、Rack にリソースを見つけるために他のルートを試すように指示するように設定されてpassいます。

Rails 3.2.x: Rack で例外を処理できる

Rails 3.2.xto_(format)では、レスポンスボディなどに行うコードはpublic_exceptions.rbにありません。html 形式のみを処理します。

おそらく、パッチを介して古いミドルウェアを新しいバージョンに置き換えることができます。

パッチを適用せずに、より具体的な方法でエラーを Rack に処理させたい場合は、José Valim の投稿の #3 を参照してください

その中で、別の回答でも言及されているように、を使用できますconfig.exceptions_app = self.routes。次に、カスタム コントローラーを指すルートを使用して、他の要求と同様に、任意のコントローラーからのエラーを処理できます。の aboutconfig.consider_all_requests_local = falseに注意してくださいconfig/environments/development.rb

を使用するためにルートを使用する必要はありませんexceptions_app。少し威圧的かもしれませんが、ハッシュを受け取り、形式が[http_status_code_number, {headers hash...}, ['the response body']]. たとえば、Rails 3.2.x 構成でこれを実行して、Rails 4.0 のようなエラーを処理できるようにする必要があります (これは最新の public_exceptions ミドルウェアが折りたたまれています)。

config.exceptions_app = lambda do |env|
  exception = env["action_dispatch.exception"]
  status = env["PATH_INFO"][1..-1]
  request = ActionDispatch::Request.new(env)
  content_type = request.formats.first
  body = { :status => status, :error => exception.message }
  format = content_type && "to_#{content_type.to_sym}"
  if format && body.respond_to?(format)
    formatted_body = body.public_send(format)
    [status, {'Content-Type' => "#{content_type}; charset=#{ActionDispatch::Response.default_charset}",
            'Content-Length' => body.bytesize.to_s}, [formatted_body]]
  else
    found = false
    path = "#{public_path}/#{status}.#{I18n.locale}.html" if I18n.locale
    path = "#{public_path}/#{status}.html" unless path && (found = File.exist?(path))

    if found || File.exist?(path)
      [status, {'Content-Type' => "text/html; charset=#{ActionDispatch::Response.default_charset}",
              'Content-Length' => body.bytesize.to_s}, [File.read(path)]]
    else
      [404, { "X-Cascade" => "pass" }, []]
    end
  end
end

注: その処理に関する問題については、フェイルセーフの実装がActionDispatch::ShowExceptions ここにあります。

Rails 3 および 4: Rails コントローラーでいくつかの例外を処理する

コントローラー自体でレンダリング エラーを発生させたい場合は、次のようにします。

def show
  respond_with @bar = Bar.where(:foo_id => params[:id].to_s).first!
rescue ActiveRecord::RecordNotFound => e
  respond_to do |format|
    format.json => { :error => e.message }, :status => 404
  end
end

ただし、エラーを発生させる必要はありません。次のこともできます。

def show
  @bar = Bar.where(:foo_id => params[:id].to_s).first
  if @bar
    respond_with @bar
  else
    respond_to do |format|
      format.json => { :error => "Couldn't find Bar with id=#{params[:id]}" }, :status => 404
    end
  end
end

また、コントローラーや ApplicationController などで、rescue_fromを使用することもできます。

rescue_from ActiveRecord::RecordNotFound, with: :not_found

def not_found(exception)
  respond_to do |format|
    format.json => { :error => e.message }, :status => 404
  end
end

また:

rescue_from ActiveRecord::RecordNotFound do |exception|
  respond_to do |format|
    format.json => { :error => e.message }, :status => 404
  end
end

一部の一般的なエラーはコントローラーで処理できますが、ルートの欠落などに関連するエラーが json などでフォーマットされている場合は、Rack ミドルウェアで処理する必要があります。

于 2013-04-16T21:19:01.013 に答える
4

index.json.builder または単にインライン json をレンダリングし、:error => 'not found' 適切な HTTP ステータスを設定することを忘れないでください。:status => 404

したがって、結果は次のようになります。

render :json => { :error => 'not found' }, :status => 422 if @bar.nil?
于 2013-04-04T22:27:58.917 に答える