Reputation: 360
I'm building a Rails 5 API and trying to handle the error response when a record is not found (i.e. /v1/users/99
but that user does not exist).
I'm using Active Model Serializer for serialization and I'm thinking there must be a way to only show the status and the error, not the stack trace. Right now, I get this:
{
status: 404,
error: "Not Found",
exception: "#<ActiveRecord::RecordNotFound: Couldn't find User with 'id'=99>",
traces: {
Application Trace: [
{
id: 1,
trace: "app/controllers/api/v1/users_controller.rb:45:in `set_user'"
}
],
Framework Trace: [
{
id: 0,
trace: "activerecord (5.0.2) lib/active_record/core.rb:173:in `find'"
},
{
id: 2,
trace: "activesupport (5.0.2) lib/active_support/callbacks.rb:382:in `block in make_lambda'"
},
{
id: 3,
trace: "activesupport (5.0.2) lib/active_support/callbacks.rb:150:in `block (2 levels) in halting_and_conditional'"
},
{
id: 4,
trace: "actionpack (5.0.2) lib/abstract_controller/callbacks.rb:12:in `block (2 levels) in <module:Callbacks>'"
},
{
id: 5,
trace: "activesupport (5.0.2) lib/active_support/callbacks.rb:151:in `block in halting_and_conditional'"
},
.
.
.
I want the error to simple on the API side, just status and error. How can this be done?
Upvotes: 1
Views: 1612
Reputation: 311
I've been doing this for logging and printing different messages in different environments.
class ApplicationController < ActionController::API
rescue_from ActiveRecord::RecordNotFound, with: :record_not_found_rescue
def record_not_found_rescue(exception)
logger.info("#{exception.class}: " + exception.message)
if Rails.env.production?
render json: {}, status: :not_found
else
render json: { message: exception, backtrace: exception.backtrace }, status: :not_found
end
end
end
I found this to be useful not only to not print back traces but you now don't need an if statement in every single controller action.
It couples well with finder methods in your controller.
def show
user = find_user
render json: user , status: :ok
end
private
def find_user
User.find(params[:id])
end
Remember you can handle different types of exceptions like this, not only the ActiveRecord::RecordNotFound
.
Upvotes: 2
Reputation: 2040
begin
user = User.find(params[:id])
rescue
render json: 'no user found', status: 404
end
render json: user, status: 200
OR you can handle exception in all controller using common handler
class ApiController < ApplicationController
around_action :handle_exceptions
def handle_exceptions
begin
yield
rescue ActiveRecord::RecordNotFound => e
status = 404
rescue ActiveRecord::RecordInvalid => e
status = 403
rescue Exception => e
status = 500
end
handle_error "#{e.message}", status unless e.class == NilClass
end
def handle_error(message, status = 500)
message = message.is_a?(Array) ? message.join('. ') : message
@errors = { message: message, status: status }
render json: @errors, :status => status
end
end
In your controller no need to write rescue, Just find the record
user = User.find(params[:id])
Upvotes: 1