In my API I use Rails' exceptions_app to handle all exceptions and make a standard JSON response. This works fine except when I raise an exception in the ApplicationController. The controller looks like this:
class ApplicationController < ActionController::Base
before_filter :raise_exception
def raise_exception
raise ActiveRecord::RecordNotFound
end
end
When I request any page I would expect to get a 404 status response. Instead of this Rails returns a 500 status. The log shows this error message:
Error during failsafe response: ActiveRecord::RecordNotFound
What does this mean and how can I work around it?
In your config/application.rb, see if the line below is commented out. If it isn't, comment it out and see if the error still persist.
config.exceptions_app = self.routes
Related
In my application_controller.rb I have the following line
rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
Now, in one method, I would like to handle that specific error differently and I've added this to one method in a controller.
class MyClass < ApplicationController
def my_method
# raising the Pundit::NotAuthorizedError in the method
authorize resource, :my_method?
rescue Pundit::NotAuthorizedError
# code on how to deal with the error
end
end
If I execute the code, the error handler from application_controller.rb will be handling my error instead of the error handler in the method.
So my question is, what is the precedence of the error handlers and is there any way I can change this, so that the error is handled in the method and not globally?
Please forget my previous answer, I myself made a mistake when reproducing your issue. In deed I am not able to reproduce it.
Please have a look at this little demo app I created:
https://github.com/andi-dev/rescue-handler-demo/blob/master/app/controllers/peas_controller.rb
The rescue_from handler is not executed, and the browser shows what I render from the rescue block within the action-method.
I've discovered that the handle_unverified_request method can be overridden in the ApplicationController to rewrite how Rails handles InvalidAuthenticityToken errors. By default the handle_unverified_request will raise the InvalidAuthenticityToken error. I've overridden this method like so
def handle_unverified_request
redirect_to '/422'
end
However, I'm using Airbrake which records errors that are raised on my Rails application. According to this answer, Rails can raise the error AND redirect the user to the 404 page. Does the same thing exist for the 422 page? I want to raise the InvalidAuthenticityToken and redirect the user to the 422 page. How do I do that?
ApplicationController.rb
protect_from_forgery with: :exception
rescue_from ActionController::InvalidAuthenticityToken, with: :rescue_422
def handle_unverified_request
raise(ActionController::InvalidAuthenticityToken)
end
def rescue_422
redirect_to '/422'
end
According to the link, you posted, Rails does not redirect the user to the 404 page, it renders 404 page instead. When InvalidAuthenticityToken error, Rails must render 422 page by default. It is done on Rack Middleware level.
If default behaviour is not what you want and you need redirect and Airbrake to log the exception, then you have to handle the exception and do the redirect AFTER! Airbrake logs it. I think that Airbrake logs exceptions on the Rake Middleware level, so you will have to somehow customize Rack Middleware's exceptions handler to get what you want. You will have to find out where Airbrake logs exceptions and make sure that your custom exceptions handler works after the logging.
Are you sure you want redirect, not render?
My Rails 4 app uses RocketPants for its JSON API and Pundit for authorization.
I have code in my /app/controllers/api/v1/base_controller.rb file to handle errors from Pundit. Whenever a user isn't authorized to update a resource, Pundit throws a NotAuthorizedError exception and I rescue it with my user_not_authorized method:
class API::V1::BaseController < RocketPants::Base
include Pundit
version 1
rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
def user_not_authorized
error! :forbidden
end
end
When I call the error! method that RocketPants provides from my exception handler, I expect to get a JSON response like this:
{
"error": "forbidden",
"error_description": "The requested action was forbidden."
}
Instead, however, calling error! just immediately blows up the request:
Completed 500 Internal Server Error in 143ms
RocketPants::Forbidden - RocketPants::Forbidden:
rocket_pants (1.13.1) lib/rocket_pants/controller/error_handling.rb:44:in `error!'
app/controllers/api/v1/base_controller.rb:61:in `user_not_authorized'
Full stack trace here.
Why isn't the error! method doing what it should when called from my Pundit exception handler?
If I put error! :forbidden in the middle of my controller action, it works as expected.
For context, the controller that inherits from base_controller.rb and calls Pundit's authorize method looks like this:
class API::V1::MealsController < API::V1::BaseController
before_filter :find_entity
def create
meal = #entity.meals.build(meal_params)
authorize(#entity, :update?)
if meal.save
expose meal, status: :created
else
expose meal.errors, status: 422
end
end
end
Apparently raising exceptions in a rescue_from is a bad idea and according to the Rails docs, exceptions raised in a handler are not bubbled up:
Exceptions raised inside exception handlers are not propagated up.
Docs: http://api.rubyonrails.org/classes/ActiveSupport/Rescuable/ClassMethods.html
Instead of re-raising RocketPants' exception, I'm simply creating and returning the JSON error message myself:
def user_not_authorized
# error! :forbidden
head 403
error = { error: 'Action not allowed.', error_description: 'Sorry, you are not allowed to perform this action.'}
expose error
end
This works!
UPDATE
I've since found an even cleaner solution: just map the Pundit exception to the RocketPants exception. This means that whenever a Pundit::NotAuthorizedError error is raised it'll be treated as a RocketPants::Forbidden error.
Got the entire solution down to a single line of code at the top of base_controller.rb:
map_error! Pundit::NotAuthorizedError, RocketPants::Forbidden
No handler required.
I want to track my user's exception with mixpanel so I added this code in my application_controller:
class ApplicationController < ActionController::Base
rescue_from Exception do |exception|
if Rails.env.production?
tracker.track(session[:mixpanel_unauth_id], 'Exception', {:exception => exception.to_s})
rescue_action_without_handler(exception)
end
end
I just want to intercept the error for mixpanel than the error must be re thrown normally.
In production a have a method not found error on rescue_action_without_handler
In development I have a blank white page when an error happen.
How to solve this problem?
I use Rails 3.2
I am building an app in Rails 4 and trying to run the server. I am getting an error involving the Application controller on line 1. Localhost will not run the server because of this error: syntax error, unexpected keyword_end, expecting end-of-input. The rack session states: ActionDispatch::Request::Session:0x1072f1ef0 not yet loaded. Here is my Application controller:
class ApplicationController < ActionController::Base
protect_from_forgery
include SessionsHelper
end
There is one more identical application frame error for another controller. Just like the Application controller, it has very little code in it and has the correct amount of 'ends'. In my "All Frames" tab, there is a slew of errors involving ActiveSupport, ActionController, and many others. There's too many to list.
Does anybody have an idea of what is causing these errors? Thanks in advance!
What if you flip those lines in the ApplicationController to look like:
class ApplicationController < ActionController::Base
include SessionsHelper
protect_from_forgery
end
I've never seen anything before the protect_from_forgery - just a hunch.
I backtracked through the errors & the application only to find out that the error was being raised through a require_user action that had a route that didn't exist.
def require_user!
redirect_to welcome_path unless signed_in?
end
I don't know why it the server didn't just tell me the error up front, but I fixed it by changing the incorrect route to a route that existed. I appreciate the people who tried to help me with this. Thanks, guys!