When you create a new Rails application, it comes with built-in error pages for certain error codes, 404, 422, 500 etc. Is there any built-in mechanism I can use to provide these errors (and others) as JSON or do I need to manually define each individual error in JSON?
for the Rails built-in pages you cannot do it because they are static inside public folder, but you can do something like:
# for 404
def not_found
# render what ever you like 404
end
# for 500
rescue_from StandardError do |exception|
# Your exception handling code here 500
end
inside you application controller
Related
When an error occurs in Rails API Only, the server responds with an error in json with the following format: {"status":500,"error":"Internal Server Error"}.
The format is the same for other errors as well: {"status":404,"error":"Not Found"}.
I want to render the errors with the format: {"errors": [{status: 404, title: "Not found"}]}.
Basically I want to change the format of all errors to be the same and I don't find a way to do that.
I know I can use (for example) rescue_from ActiveRecord::RecordNotFound, with: :my_method and override the single exceptions but this means I have to list all possible exceptions and return the appropriate code, when Rails is already doing that.
What I am looking for is a method that can be overridden, and that I can use to change the "default" error format of Rails.
The best way to do this in Rails is probably defining an exceptions_app (see config.exceptions_app) which must be a custom rack app. This will let you render your exceptions however you choose. An example can be found here.
config.exceptions_app = ->(env) { ErrorsController.action(:show).call(env) }
class ErrorsController < ApplicationController
layout 'error'
def show
exception = env['action_dispatch.exception']
status_code = ActionDispatch::ExceptionWrapper.new(env, exception).status_code
# render whatever you want here
end
end
You can also check what the default implementation by rails is.
There is also a gem called exception_handler that might be an alternative here.
More resources: https://devblast.com/b/jutsu-12-custom-error-pages-in-rails-4
You can configure an exceptions_app in Rails.
The default Middleware which manages exceptions and render the errors is ActionDispatch::PublicExceptions.
First thing is to override this middleware to implement our custom behavior. Inside app/middlewares/action_dispatch folder create a public_exceptions_plus.rb file with the following:
module ActionDispatch
class PublicExceptionsPlus < PublicExceptions
def call(env)
request = ActionDispatch::Request.new(env)
status = request.path_info[1..-1].to_i
content_type = request.formats.first
# define here your custom format
body = { errors: [{ status: status,
title: Rack::Utils::HTTP_STATUS_CODES.fetch(status, Rack::Utils::HTTP_STATUS_CODES[500]) }] }
render(status, content_type, body)
end
end
end
after that, inside config/application.rb add the following:
config.exceptions_app = ->(env) { ActionDispatch::PublicExceptionsPlus.new(Rails.public_path).call(env) }
Thanks #smallbutton.com for the input. I think this is a better solution since it does not require a controller but just a middleware.
If a parameter that's required is missing using strong parameters, the Rails server will respond with an HTTP 500.
This does not give me control over giving the user feedback with what exactly went wrong. Does it not make sense to be able to send them back a message such a required parameter is missing?
What is the "Rails way" of giving appropriate user feedback on ActionController::ParameterMissing? Is one supposed to capture the exception and handle your request response there? It seems wrong to do that in every controller.
You can use
rescue_from ActionController::ParameterMissing do |e|
render 'something'
end
in your ApplicationController (or whatever your parent controller is).
As to whether you should inform users or not, I think it depends on what your controllers are doing. If they are API controllers, it definitely makes sense to handle that gracefully, as the users are responsible for preparing the input.
If they are accepting data from your HTML forms it's, in my opinion, not that important as the missing parameters probably mean that the user tinkered with the HTML, or something went really wrong inside the browser.
Since you mention wanting to communicate the error specifics back to the user, you could do something like the following:
# app/controllers/application_controller.rb
rescue_from ActionController::ParameterMissing do |exception|
render json: { error: exception.message }, status: :bad_request
end
You can also define a method to handle a specific exception, if you'd prefer to break up the handling logic:
# app/controllers/application_controller.rb
rescue_from ActionController::ParameterMissing, with: :handle_parameter_missing
def handle_parameter_missing(exception)
render json: { error: exception.message }, status: :bad_request
end
Both of the above examples will return a JSON response like so: {"error"=>"param is missing or the value is empty: [field_name]"}
For an API-only application, I think this is valuable information to pass on.
More info:
Rails API rescue_from documentation
Handling Errors in an API Application the Rails Way
When Rails responds to an HTTP request, it's HTTP response will always be correctly headed up with an appropriate HTTP response code. This can be for successful operations (a 2xx code), such as the creation of a new ActiveRecord in the database, or for errors (a 4xx). In the latter case a rendered HTML page is supplied containing information about the error (a backtrace, etc).
My app requires that all Rails HTTP responses take the form of JSON, so I am writing my own code to render these HTTP responses accordingly. A number of tutorials talk about writing something like this to render such responses (in this example, located in user_account_controller.rb where UserAccount is the name of an ActiveRecord model):
# method to create a UserAccount object based on supplied user_account_params
def create
#user_account = UserAccount.create!(user_account_params)
if #user_account
render :status => 201, :json => {
'message' : 'Operation was successful!"
}
end
end
And, if an exception is thrown, it is possible to bind a customer exception handler as follows:
# In ApplicationController
rescue_from Exception, :with => :json_exception_handler
def json_exception_handler(exception)
render :status => 422, :json => {
:exception => {
:backtrace => exception.backtrace,
:message => exception.message
}
}
end
The problem with both of these solutions is that they require me to statically set the HTTP response codes to have the same value every time (201 and 422 in these examples). Rails already does a great job of automatically picking the best response code to use, depending on the type of error or type of successful operation. I don't want to have to reinvent the wheel badly by inventing my own response code structure for each error and operation in my app.
So my question is this: how do I supply the response code that Rails will have automatically chosen anyway, whilst retaining the ability to print out custom JSON?
I have just barely gotten into Ruby / ROR but need to quickly write a class for handling errors and doing something with them. I've been able to find the important examples/tutorials for the rest of what I need but I'm having trouble finding what the best alternative to PHP's "set_error_handler" is.
My goals are:
I'd like to write a class that will capture any ruby-level errors automatically.
I'd like for the class to also be called by the user when there are custom errors/exceptions to report.
I'd like this work for any ruby app, but my main focus is for ruby-on-rails applications as well. Thanks for your advice.
I think the closest equivalent in Rails is rescue_from - it allows you to specify code will catch any given exception (except some template errors - though there are ways round that). If you want, you could then hand it off to some other class. So I guess what you'd do in your case would be:
in app/controllers/application_controller.rb:
class ApplicationController < ActionController::Base
rescue_from Exception do |e|
MyExceptionHandler.handle_exception(e)
end
end
in lib/my_exception_handler.rb:
class MyExceptionHandler
def self.handle_exception exception
# your code goes here
end
end
If that helps, let me know and I'll dig out the link to how you catch template errors.
begin
#require all_kinds_of_things
"abc".size(1,2)
123.reverse
# rest of brilliant app
rescue Exception => e #Custom, catch-all exeption handler
puts "Doh...#{e}"
print "Do you want the backtrace? (Y) :"
puts e.backtrace if gets.chomp == "Y"
end
Define ApplicationController#rescue_in_public(exception) and put your custom handling code there.
This augments Rails' default exception handling at the top level - right before the HTTP response is generated. As your Rails apps grow in complexity and use external resources, there will be more exceptions that you'll want to handle much closer to where the exceptions are thrown, but this can get you started.
This method will only work on HTTP requests and will not catch exceptions in any custom rake tasks you create or code executed via rails runner.
Here's an example from one of my applications:
class ApplicationController < ActionController::Base
...
protected
def rescue_action_in_public (exception)
case exception
when ActionController::InvalidAuthenticityToken
if request.xhr?
render :update do |page|
page.redirect_to '/sessions/new/'
end
else
redirect_to '/sessions/new/'
end
when ActionController::NotImplemented
RAILS_DEFAULT_LOGGER.info("ActionController::NotImplemented\n#{request.inspect}")
render :nothing => true, :status => '500 Error'
else
super
end
end
end
What I want to do: redirect the user to a special error page if the database is down.
I'm using an Oracle database with the OCI adapter.
It appears that, if the database is unavailable (say, down for backup), that the OCI adapter throws an error before I ever hit a controller (based on the stack trace, it's while it's setting up the connection pool). So I can't use rescue_from or rescue_action, even in ApplicationController -- the rescue line is never reached.
Is there any way to "wrap" the initialization or otherwise rescue a particular error at a higher level (or earlier point) than ApplicationController?
Try overriding ActionController::Failsafe, or sticking another middleware before it on the stack, you should be able to catch almost all exceptions from there, and 302 at will.
Example:
class DbFailsafe
def new(app)
#app = app
end
def call(env)
#app.call(env)
rescue DataBaseError => e
[302, {"Content-Type" => "text/plain", "Location" => "http://otherserver.com/sorrt-the-site-is-down.html"}, ["You are being redirected to http://otherserver.com/sorrt-the-site-is-down.html"]]
end
end
and in your environment.rb
config.middleware.insert_after ::ActionController::Failsafe, DbFailsafe
I'd recommend (because a we should be returing a 5xx not a 3xx) rendering a simple page with a js redirect. This is simple to do, just edit public/500.html.