I\'m building a Ruby on Rails api using Ruby 2.0 and Rails 4.0. My app is almost solely a JSON API, so if an error occurs (500, 404), I want to capture that error and return
Try this if you want respond to all types of errors in the same way
rescue_from StandardError, :with => :error_render_method
If you don't want this behavior in your development mode, add the above code under
unless Rails.application.config.consider_all_requests_local
The request isn't even hitting your app.
You need to define a catchall route so Rails will send the request to your app rather than display an error (in development) or render the public/404.html page (in production)
Modify your routes.rb file to include the following
match "*path", to: "errors#catch_404", via: :all
And in your controller
class ErrorsController < ApplicationController
def catch_404
raise ActionController::RoutingError.new(params[:path])
end
end
And your rescue_from
should catch the error then.
After trying a few variations I've settle on this as the simplest way to handle the API 404s:
# 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
this works in rails4, this way you can manage directly all errors: for example you can render error_info as json when an an error occurs from an api call..
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
I used the 404.html from public folder and this is in dev environment.
I actually got the answer from:
However, I did a little experiment on what pieces of code actually made it work. Here's are the pieces of code that I only added.
config/routes.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
Will appreciate any comments and clarifications as to why some of the original are are needed. For instance, using this line of code
raise ActionController::RoutingError.new(params[:path])
and this
rescue_from ActionController::RoutingError, :with => :error_render_method
Because rescue_from
and raise ActionController::RoutingError
seem to be the popular answer from the older Rails versions.