Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 295
coorasse Avatar asked Sep 13 '18 12:09

coorasse


2 Answers

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

like image 145
smallbutton Avatar answered Oct 13 '22 01:10

smallbutton


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.

like image 42
coorasse Avatar answered Oct 13 '22 01:10

coorasse