I have a Rails app for providing a Json REST-Api. In my ApplicationController, I have these lines to catch various errors:
rescue_from ActiveRecord::RecordNotFound, with: :_404
rescue_from ActiveRecord::RecordInvalid, with: :_400
rescue_from ActiveRecord::RecordNotUnique, with: :_406
....
def _404(exception)
Rails.logger.error "head 404 with params #{params}"
render status: 404, :json => {:error => exception.message}
end
In one of my controllers, I have a statement like those where I query the db multiple times for different records.
#account = Account.find_by_phone(params[:phone])
#controller = Controller.find_by_controller_id(params[:role_id])
#batlockers_rel = Batlocker.includes(:customer).where("customer_id is not null")
When I make a call into the controller hitting the specific action, I get the header and the response body
HTTP/1.1 501 NOT IMPLEMENTED
{"error":"undefined method `[]' for nil:NilClass"}
for the requested parameters which are not linked to any database objects.
How do I catch these NilErrors, and how can I then include the record which was not found?
Possibly capture NameError class or more generally StandardError:
rescue_from ::NameError, with: :_whatever
rescue_from StandardError, with: :_whatever
But this types of errors you need to handle yourself, of-cource you need to capture for the first time but you should resolve the error, your app not supposed to throw this kind of errors, check if the data is there before access elements.
Related
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
I want to return from my controller if either a validation failed or a parameter is missing with 400 - bad request. So in my controller if have
if params["patch"].nil? then
raise ActionController::BadRequest.new( "The Json body needs to be wrapped inside a \"Patch\" key")
end
and i catch this error in my Application Controller with:
rescue_from ActionController::BadRequest, with: :bad_request
def bad_request(exception)
render status: 400, json: {:error => exception.message}.to_json
end
But it seems like i cannot add custom messages when raising ActionController::BadRequest. Because when passing an invalid body the response is only {"error":"ActionController::BadRequest"} and not the hash i provided.
In the console i get the same behaviour. raise ActiveRecord::RecordNotFound, "foo" indeed raises ActiveRecord::RecordNotFound: foo.
But raise ActionController::BadRequest, "baa" results in
ActionController::BadRequest: ActionController::BadRequest
How can i add custom messages to the BadRequest exception?
Try this:
raise ActionController::BadRequest.new(), "The Json body needs to be wrapped inside a \"Patch\" key"
I am attempting to rescue RuntimeErrors from within my controller using rescue_from, log it as a HealthNotice, and then redirect the user with a flash message, but I'm finding that in certain circumstances, the rescue_from method is being run twice (I end up with 2 duplicate HealthNotices). If the exception is rasied from a function called in a method (such as the test method below), it only creates one, but if I raise the exception directly within the method (such as test2), the rescue function is running twice (although strangely I don't get a double render error -- ??)
class Portal::BluerimController < PortalController
rescue_from RuntimeError, with: :error_handler
def error_handler(e)
hn_long_message = "#{e.class}: #{e.message}\n#{e.backtrace.join("\n")}\n\nPARAMS:\n#{params}"
HealthNotice.create(name: "API Exception Handled", severity: "WARNING", short_message: e.message, long_message: hn_long_message)
flash[:notice] = e.message
redirect_to :action => :index and return
end
def test
Sotal.new.test # a method that raises a RuntimeError -- only 1 HealthNotice
end
def test2
raise "an error" # results in duplicate HealthNotice
end
end
Any suggestions on what I'm doing wrong? Thanks!
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.
So I handle exceptions with an error controller to display dynamic content to my users in production. I have it in my route file to do:
# Errors
%w( 404 422 500 ).each do |code|
get code, :to => "errors#show", :code => code
end
The only problem is now that I'm routing on errors such as that I lose information in my controller when I want to notify Airbrake. How can I maintain the exception information and send it to Airbrake on a 500? Right now all I get is the env that was occurring at the time of the exception which is less helpful for debugging purposes.
class ErrorsController < ApplicationController
def show
notify_airbrake(env)
render status_code.to_s, :status => status_code
end
protected
def status_code
params[:code] || 500
end
end
Are you handling an error by redirecting to a URL like http://your.site/500? That will be just an HTTP request like any other, losing the exception context you're after. Instead, you probably want to be using ActionController's Rescue functionality. It would look like this:
class ApplicationController < ActionController::Base
rescue_from StandardError, with: :render_error
private
def render_error(error)
notify_airbrake(error)
render text: 500, status: 500
end
end
You can add multiple rescue_from declarations to handle different kinds of error, like the ActiveRecord::RecordNotFound from the Rails guide's example.