coorasse
coorasse

Reputation: 5528

Override default json format for errors in Rails API

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.

Upvotes: 5

Views: 2169

Answers (2)

coorasse
coorasse

Reputation: 5528

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.

Upvotes: 3

smallbutton
smallbutton

Reputation: 3437

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

Upvotes: 7

Related Questions