Ruby on Rails api using Ruby 2.0 and Rails 4.0。私のアプリはほとんどJSONAPIだけなので、エラーが発生した場合(500、404)、そのエラーをキャプチャして、適切にフォーマットされたJSONエラーメッセージを返します。
私は試しました this そしてまた:
rescue_from ActionController::RoutingError, :with => :error_render_method
def error_render_method
puts "HANDLING ERROR"
render :json => { :errors => "Method not found." }, :status => :not_found
true
end
私のApplicationControllerで。
これらはどちらもトリックを行いません(例外はまったくキャプチャされません)。私のグーグルは、これが3.1と3.2の間で大きく変化したことを示しており、Rails 4.0でこれを行う方法に関する適切なドキュメントを見つけることができません。
誰か知ってる?
編集404ページに移動したときのスタックトレースは次のとおりです。
Started GET "/testing" for 127.0.0.1 at 2013-08-21 09:50:42 -0400
ActionController::RoutingError (No route matches [GET] "/testing"):
actionpack (4.0.0) lib/action_dispatch/middleware/debug_exceptions.rb:21:in `call'
actionpack (4.0.0) lib/action_dispatch/middleware/show_exceptions.rb:30:in `call'
railties (4.0.0) lib/Rails/rack/logger.rb:38:in `call_app'
railties (4.0.0) lib/Rails/rack/logger.rb:21:in `block in call'
activesupport (4.0.0) lib/active_support/tagged_logging.rb:67:in `block in tagged'
activesupport (4.0.0) lib/active_support/tagged_logging.rb:25:in `tagged'
activesupport (4.0.0) lib/active_support/tagged_logging.rb:67:in `tagged'
railties (4.0.0) lib/Rails/rack/logger.rb:21:in `call'
actionpack (4.0.0) lib/action_dispatch/middleware/request_id.rb:21:in `call'
rack (1.5.2) lib/rack/methodoverride.rb:21:in `call'
rack (1.5.2) lib/rack/runtime.rb:17:in `call'
activesupport (4.0.0) lib/active_support/cache/strategy/local_cache.rb:83:in `call'
rack (1.5.2) lib/rack/lock.rb:17:in `call'
actionpack (4.0.0) lib/action_dispatch/middleware/static.rb:64:in `call'
railties (4.0.0) lib/Rails/engine.rb:511:in `call'
railties (4.0.0) lib/Rails/application.rb:97:in `call'
rack (1.5.2) lib/rack/lock.rb:17:in `call'
rack (1.5.2) lib/rack/content_length.rb:14:in `call'
rack (1.5.2) lib/rack/handler/webrick.rb:60:in `service'
/System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/Ruby/2.0.0/webrick/httpserver.rb:138:in `service'
/System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/Ruby/2.0.0/webrick/httpserver.rb:94:in `run'
/System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/Ruby/2.0.0/webrick/server.rb:295:in `block in start_thread'
Rendered /Library/Ruby/Gems/2.0.0/gems/actionpack-4.0.0/lib/action_dispatch/middleware/templates/rescues/_trace.erb (1.0ms)
Rendered /Library/Ruby/Gems/2.0.0/gems/actionpack-4.0.0/lib/action_dispatch/middleware/templates/routes/_route.html.erb (2.9ms)
Rendered /Library/Ruby/Gems/2.0.0/gems/actionpack-4.0.0/lib/action_dispatch/middleware/templates/routes/_route.html.erb (0.9ms)
Rendered /Library/Ruby/Gems/2.0.0/gems/actionpack-4.0.0/lib/action_dispatch/middleware/templates/routes/_table.html.erb (1.1ms)
Rendered /Library/Ruby/Gems/2.0.0/gems/actionpack-4.0.0/lib/action_dispatch/middleware/templates/rescues/routing_error.erb within rescues/layout (38.3ms)
私はそれがこれまでに到達することを望んでいないと思います、何かがそれをキャッチして適切なjsonエラー応答を返す必要があります。
リクエストはアプリにヒットしていません。
Railsが(開発中の)エラーを表示したり、(本番環境の)public/404.htmlページをレンダリングしたりするのではなく、アプリにリクエストを送信するように、キャッチオールルートを定義する必要があります。
ルート.rbファイルを変更して、以下を含めます
match "*path", to: "errors#catch_404", via: :all
そしてあなたのコントローラーで
class ErrorsController < ApplicationController
def catch_404
raise ActionController::RoutingError.new(params[:path])
end
end
そしてあなたの rescue_from
はエラーをキャッチする必要があります。
いくつかのバリエーションを試した後、API404を処理する最も簡単な方法としてこれを決定しました。
# Passing request spec
describe 'making a request to an unrecognised path' do
before { Host! 'api.example.com' }
it 'returns 404' do
get '/nowhere'
expect(response.status).to eq(404)
end
end
# routing
constraints subdomain: 'api' do
namespace :api, path: '', defaults: { format: 'json' } do
scope module: :v1, constraints: ApiConstraints.new(1) do
# ... actual routes omitted ...
end
match "*path", to: -> (env) { [404, {}, ['{"error": "not_found"}']] }, via: :all
end
end
これはRails4で機能します。このようにして、すべてのエラーを直接管理できます。たとえば、API呼び出しからエラーが発生したときに、error_infoをjsonとしてレンダリングできます。
application_controller.rb
class ApplicationController < ActionController::Base
protect_from_forgery
# CUSTOM EXCEPTION HANDLING
rescue_from StandardError do |e|
error(e)
end
def routing_error
raise ActionController::RoutingError.new(params[:path])
end
protected
def error(e)
#render :template => "#{Rails::root}/public/404.html"
if env["ORIGINAL_FULLPATH"] =~ /^\/api/
error_info = {
:error => "internal-server-error",
:exception => "#{e.class.name} : #{e.message}",
}
error_info[:trace] = e.backtrace[0,10] if Rails.env.development?
render :json => error_info.to_json, :status => 500
else
#render :text => "500 Internal Server Error", :status => 500 # You can render your own template here
raise e
end
end
# ...
end
routes.rb
MyApp::Application.routes.draw do
# ...
# Any other routes are handled here (as ActionDispatch prevents RoutingError from hitting ApplicationController::rescue_action).
match "*path", :to => "application#routing_error", :via => :all
end
パブリックフォルダーの404.htmlを使用しましたが、これは開発環境にあります。
私は実際に以下から答えを得ました:
ただし、実際にどのコードが機能するかについて少し実験しました。これが私が追加しただけのコードです。
config/routers.rb
Rails.application.routes.draw do
// other routes
match "*path", to: "application#catch_404", via: :all
end
app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
def catch_404
render :file => 'public/404.html', :status => :not_found
end
end
オリジナルの一部が必要な理由についてのコメントや説明をいただければ幸いです。たとえば、このコード行を使用します
raise ActionController::RoutingError.new(params[:path])
この
rescue_from ActionController::RoutingError, :with => :error_render_method
なぜならrescue_from
およびraise ActionController::RoutingError
古いRailsバージョンからの人気のある答えのようです。
すべてのタイプのエラーに同じ方法で対応したい場合は、これを試してください
rescue_from StandardError, :with => :error_render_method
開発モードでこの動作を望まない場合は、上記のコードを下に追加してください
unless Rails.application.config.consider_all_requests_local