I built an error handler that, when there's an error in any controller method in production, reroutes the user to an error page and sends me, the developer, a notification email. This works, but I want the normal error screen to appear when I'm in development. I assumed the code to produce this was just raise e, but instead in development I'm now getting the default production error page (the one that says "We're sorry, but something went wrong".), instead of the detailed error message and trace that used to appear.
class ApplicationController < ActionController::Base
rescue_from StandardError, with: :handle_error
#error handler
def handle_error(e)
if Rails.env.production?
#code to send email and redirect to error page
else
raise e
end
end
end
I also tried the following:
raise
raise StandardError.new e
raise e, e.message
raise e.message
and if I run any of those in a binding.pry console, they produce the error message I'm looking for, but the error page still just says "We're sorry, but something went wrong."
Anyone know how I can just show the default development error page?
UPDATE
This is insane...so the code to display an error normally should definitely work, but something somewhere is preventing that. If I change config.consider_all_requests_local = true on production, the errors show up on production, but then even if I copy and paste my config/environments/production.rb file into my config/environments/development.rb, the errors still don't show on development. If I enter a pry console, request.local? returns "0", signifying true, and Rails.env returns "development". I have no idea what is going on.
UPDATE 2
Apparently I'm not supposed to be rescuing exceptions on development, but even if I delete every bit of custom error handling code so my Application Controller is just empty, my errors still don't show on development. Further, I have a different app with the same exact error handling code, and for that the errors do show.
Search your code for consider_all_requests_local, it's this configuration that show the full error log.
It must be set as true on your development.rb config file. It's either missing from your configs, or other config is overwriting it
This is not "insane", this is completely expected behavior. You cannot raise from within a rescue_from handler. That would cause an infinite loop.
You also cannot rescue_from StandardError as stated specifically in the documentation:
Using rescue_from with Exception or StandardError would cause serious side-effects as it prevents Rails from handling exceptions properly. As such, it is not recommended to do so unless there is a strong reason.
Instead of conditionally handling the exception inside your rescue_fromhandler, you should conditionally bind the handler, and pick a more specific exception class to handle.
class ApplicationController < ActionController::Base
rescue_from StandardError, with: :handle_error if Rails.env.production?
#error handler
def handle_error(e)
#code to send email and redirect to error page
end
end
create ErrorsController like below
class ErrorsController < ApplicationController
skip_before_action :login
def not_found
respond_to do |format|
format.html { render status: 404 }
format.json { render json: { error: "Not found" }, status: 404 }
end
end
end
Related
This question already has answers here:
rescue_from ActionController::RoutingError doesn't work
(2 answers)
Closed last month.
In my Rails 7 API only mode, I'm trying to catch case when someone is trying to make a request into api/v1/mandate instead of api/v1/mandates (classic ActionController::RoutingError case). In such case it will render the 404 error with some build in stuff as JSON response. I want to customize that JSON error answer inside my BaseController. I was trying:
#base_controller.rb
module Api
module V1
class BaseController < ActionController::API
rescue_from ActionController::RoutingError, with: :render_not_found_error
private
def render_not_found_error(exception)
render json: { error: true, message: exception.message }, status: :not_found
end
end
end
end
I've also have:
#config/environments/development.rb
config.consider_all_requests_local = true
But nothing changed, still doesn't get my JSON error.
You can't rescue routing errors in a controller because the exceptions occur in the routing layer, not the controller.
For missing routes, you need a "catch-all" route that can capture them and then forward that to a controller which performs your "exception" handling.
In practice this often shakes out as a NotFoundController with different actions depending on the type of route being handled.
When raising an exception in rails, I would like to add a custom response as well.
For example if Im making a custom 404 exception, then i would like the response to be something like msg: "no record found. I was thinking of doing something like this:
raise customError, "msg: no record found"
but that doesnt seem to work. Is there another way I can go about this?
You could use rescue_from to rescue all your customErrors in your controller, and then render the response
class ApplicationController
rescue_from CustomError do |exception|
render_json json: { msg: exception.message }, status: 404
end
end
Since my tests (and dev output) were getting a bit out of hand, I modified the plain old set_user filter of my UsersController like
def set_user
#user = User.find(params[:id]) rescue render(json: { message: 'User not found' }, status: 404)
end
which (crudely) gets rid of the ActiveRecord::RecordNotFound exception and proceeds to render a plain old 404 message. Now, except for the warning I get regarding the filter halting processing, there seem to be no visible side effects, and I can avoid rescuing the AR exception in all my spec examples.
I'm wondering if this is an acceptable practice, and, if not, what a better solution might look like.
As you already know, this is quite crude. It will catch any exception and return the JSON you specified, although it might not be a ActiveRecord::RecordNotFound error.
I would recommend using rescue_from ActiveRecord::RecordNotFound, with: :method_name in your ApplicationController, which will rescue just that type of error and respond with the appropriate JSON.
I have a Rails web app with some forms users can use to comment etc. They are frequently being used by spammers trying to create spam comments. Nothing new about that.
Sometimes I get CSRF-hack attempts which causes an ActionController::InvalidAuthenticityToken exception. This happens quite a lot and I would like to rescue and send the user/bot to a "You failed to comment"-page.
This has turned out to be a tricky exception to catch though since I can't recreate the error myself. I first put a rescue in the #create when the model (created by the form) was to be saved but it doesn't catch the exception. In order to do so I consider having it cover the entire controller section but that seems over the top.
My two questions:
Is there a way to re-create an ActionController::InvalidAuthenticityToken error by myself, so I can test it?
When is the exception raised? During .save? On Comment.new? Basically, where should I put my begin/rescue?
Thanks!
You can rescue from this exception in your controller. One way you can set this up is to add a rescue in your ApplicationController.
class ApplicationController < ActionController::Base
rescue_from ActionController::InvalidAuthenticityToken, :with => :invalid_auth_token
private
def record_not_found
render :text => "You failed to comment", :status => 422
end
end
You could also just capture the exception locally in your controller action.
def create
begin
# Create your comment
rescue ActionController::InvalidAuthenticityToken
# Render your last view with some error text.
end
end
You can add raise ActionController::InvalidAuthenticityToken inside your action if you want to test it out.
Looks like a bug with rails but want to make sure I didn't miss anything before reporting a bug.
I am trying to rescue from ActionController::InvalidAuthenticityToken in the application controller with the following code:
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
rescue_from ActionController::InvalidAuthenticityToken, with: :show_errors
....
private
def show_errors
redirect_to root_path, alert: "Cookies are disabled"
end
end
The redirect works fine, but the alert message does not show. The flash hash is empty in the new page: #<ActionDispatch::Flash::FlashHash:0x007f9dbdb5c1d0 #discard=#<Set: {}>, #flashes={}, #now=nil>
There are no other filters in the application controller that could be affecting the hash / causing another redirect. The logs show only one redirect as expected.
Tried flash.keep[:alert] = .. and flash.now[:alert] = .. as well; no luck.
Getting this behavior on two different rails apps, one with 4.2.0 and another with 4.1.6.
Try
def show_errors
flash[:error] = "Cookies are disabled"
redirect_to root_path
end
Andrew White gave an explanation on what is happening on the issue I created:
Since flash is dependent on the session and the session is dependent
on cookies working then it's never going to work if you think about
it, :-)
I'd suggest redirecting to a custom url or add a query param to the
url that triggers the display of the message.
I am redirecting to a custom url as suggested.