Ziyan Junaideen
Ziyan Junaideen

Reputation: 3310

Sinatra Catching Exceptions for Custom Error Pages

I am trying to handle errors in a Modular Sinatra App. We raise various error through out the app and I wrote some thing alike to catch errors thinking it will happen hierarchically.

My the file I use to error handle looks like the following.

#!/usr/bin/env ruby

# @class        class RApp
# @brief        Sinatra main routes overloading for App class
class RApp < Sinatra::Base

  # @fn         not_found do {{{
  # @brief      Not found error page
  not_found do
    render_error 404, _('Not Found'), env['sinatra.error'].message
  end # }}}

  # @fn         error ServiceNotAvailableError do {{{
  # @brief      Handle ServiceNotFoundException, commonly associated with communication
  #             errors with external required services like Ambrosia, Saba
  error ServiceNotAvailableError do
    render_error 503, _('Service Not Available'), env['sinatra.error'].message
  end # }}}

  # @fn         error Exception do {{{
  # @brief      Handle general internal errors
  error Exception do
    render_error 500, _('Internal Server Error'), env['sinatra.error'].message
  end # }}}

  error DBC::InvalidUUIDError do
    "Invalid UUID Error"
  end

  # @fn         def show_error code, title, message, view = nil {{{
  # @brief      Displays the proper message (html or text) based on if the request is XHR or otherwise
  def render_error code, title, message, view = nil
    view = code.to_s if view.nil?

    if request.xhr?
      halt code, message
    else
      halt code, slim(:"error_pages/#{view}", locals: {title: title, message: message})
    end
  end # }}}

  # Just for testing
  get '/errors/:type' do |type|
    raise Object.const_get(type)
  end

end # of class RApp < Sinatra::Base }}}

# vim:ts=2:tw=100:wm=100

I was thinking it will try in the order in the file which seem to be true.

How ever the issue is Exception doesn't catch all exceptions.

For example I have DBC::InvalidUUIDError which is as such

But it error Exception doesn't catch all Exception as I thought.

Am I doing some thing wrong? Or is it not possible to catch all exceptions generally?

Note: Additionally to the answers provided (both works), I had set :raise_errors, true. You need to not set it in development and production. By default it is set for 'test' env.

Nature of the matter was some exceptions been handled and some not.

Upvotes: 1

Views: 3727

Answers (2)

Martin Konecny
Martin Konecny

Reputation: 59681

Here is the relevant info from the Sinatra docs:

Sinatra installs special not_found and error handlers when running under the development environment to display nice stack traces and additional debugging information in your browser.

What this means, is that when you are running in development, Sinatra has a creates a default "catch-all" error handler which has a higher precedence than your error Exception do .. end block.

Once you enter production mode, (or disable in dev via disable :show_exceptions ), your error Exception do .. end block should catch all of your exceptions.

Note that the order of these error blocks defined is irrelevant.

Upvotes: 3

Rennex
Rennex

Reputation: 599

Add this to prevent Sinatra's own error page from interfering with your custom error handlers:

set :show_exceptions, false

By default, the setting is true in development mode, and false in production.

Note that if you follow the Sinatra readme's advice about setting set :show_exceptions, :after_handler, that will enable your error handlers to run even in development mode (at least for some exception classes), but it will also enable the built-in error page in production for uncaught exceptions. And it's unclear to me which error handlers it will respect, and which ones it will ignore in favor of the built-in debug page.

Edit: I realised you also asked about the order the error handlers are defined in. It doesn't matter; Sinatra looks for exact matches to the exception class in the app class first, and then in its superclasses. If it doesn't find any, it repeats the search for the exception's superclass, etc. So the handler for Exception will only get called for exceptions for which there isn't a closer match.

Upvotes: 4

Related Questions