stevenpslade
stevenpslade

Reputation: 360

I don't want to send stack trace errors in Rails API

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

Answers (2)

Iaan Krynauw
Iaan Krynauw

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

ashvin
ashvin

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

Related Questions