rails how add a response when raising a custom exception - ruby-on-rails

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

Related

Rails default error page doesn't show on development

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

Where's the translation for "Couldn't find <Model>" when I raise ActiveRecord::RecordNotFound?

I tried setting the following in my en.yml, but it still shows the original translation.
en:
activerecord:
errors:
messages:
record_invalid: "%{errors}"
exceptions:
not_found: "%{model_name} not found" // I thought this was the one
Any idea how to change the error message?
You can't change the exception message, it's hard-coded inside Rails
https://github.com/rails/rails/blob/6f0cda8f8e208143cbd3b39e786521c2e5cddb7a/activerecord/lib/active_record/core.rb#L174
Depending on your case you could do something like this:
class ApplicationController < ActionController::Base
rescue_from 'ActiveRecord::RecordNotFound' do |exception|
render json: { message: "#{exception.model} not found" }, status: 500
end
end

How to handle bad requests / wrong parameters in a rails api?

For example, an api consumer sends:
"ticket": [{ "param": "value"}]
The controller does:
params.require(:ticket).permit(:name)
This would return a 500 error: "Undefined method permit for array"
Is there a DRY / best practice way to handle this? I think a status 400 should be returned instead.
I think the strong parameters gem should handle this internally, but they don't, so here is my solution
rescue_from NoMethodError do |exception|
if exception.message =~ /undefined method `permit' for/
render_error(message: 'Invalid parameter format.', type: :invalid_parameters, status: :bad_request)
else
raise
end
end
Based on your solution .. you would want to handle all the possibilities verse a specific error response. You can set in a rescue in your render method like this and set unauthorized if there is a method error or just something else going wrong :
begin
render json: json, status: 200
rescue_from NoMethodError do |exception|
render :unauthorized
end
rescue => e
render :unauthorized
end

Rails 4.2 ActionController:BadRequest custom error message

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"

Get exception content in Rails Router

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.

Resources