module Strelka::App::Errors
Custom error-handling plugin for Strelka::App
.
plugin :errors # Handle only status 400 errors on_status HTTP::BAD_REQUEST do |res, status_info| # Do something on 400 errors end # Handle any other error in the 4xx range on_status 400..499 do |res, status_info| # Do something on 4xx errors end
If you have the :templating
plugin loaded, you can substitute a Symbol that corresponds with one of the declared templates instead:
class MyApp < Strelka::App plugins :errors, :templating layout 'layout.tmpl' templates :missing => 'errors/missing.tmpl' # Load the 'missing' template, wrap it up in the layout # template and render that as the body of the 404 # response on_status HTTP::NOT_FOUND, :missing end # class MyApp
The template can access the status info hash via the :status_info
note on the request or response object:
<!-- error.tmpl --> Sorry, there was a <?call request.notes[:status_info][:status] ?> error: <?escape request.notes[:status_info][:message] ?>.
If you want to do something application-specific for an error, but still use the default error-handling, just call finish_with again with the status info from your status handler:
class MyApp < Strelka::App plugins :errors # Send an email when an app is going to return a 500 error on_status HTTP::SERVER_ERROR do |res, info| require 'mail' Mail.deliver do from 'app@example.com' to 'team@example.com' subject "SERVER_ERROR: %p [%s]" % [ self.class, self.class.version_string ] body "Server error while running %p [%s]: %s" % [ self.class, self.conn, status.message ] end # Finish the transaction finish_with( info ) end def handle( req ) finish_with( HTTP::SERVER_ERROR, "Ack! Something bad happened." ) end end # class MyApp
See the documentation for ClassMethods.on_status
for more details on the status-handler block.
Constants
- DEFAULT_HANDLER_STATUS_RANGE
The range of status codes to delegate to an on_status handler that doesn't specify one
Public Instance Methods
Check for a status response that is hooked, and run the hook if one is found.
# File lib/strelka/app/errors.rb, line 143 def handle_request( request ) self.log.debug "[:errors] Wrapping request in custom error-handling." response = nil # Catch a finish_with; the status_response will only be non-nil status_response = catch( :finish ) do # Provide our own exception-handling and translate them into server errors begin response = super rescue => err self.log.error "%s: %s %s" % [ err.class.name, err.message, err.backtrace.first ] err.backtrace[ 1..-1 ].each {|frame| self.log.debug(' ' + frame) } finish_with( status: HTTP::SERVER_ERROR, message: err.message, headers: {}, backtrace: err.backtrace, exception: err ) end nil end # If the app or any plugins threw a finish, look for a handler for the status code # and call it if one is found. response = self.handle_status_response( request, status_response ) if status_response return response end
Return a response built out of the status_info
for the specified request
if there is a custom handler. If there isn't one, rethrows to the main :finish handler.
# File lib/strelka/app/errors.rb, line 178 def handle_status_response( request, status_info ) status = status_info[:status] # If we can't find a custom handler for this status, re-throw # to the default handler instead handler = self.status_handler_for( status ) or throw( :finish, status_info ) self.log.debug "Custom %d handler: %p" % [ status, handler ] # Set up the request's response with the right status code request.notes[:status_info] = status_info response = request.response response.status = status # The handler is an UnboundMethod, so bind it to the app instance # and call it return handler.bind( self ).call( response, status_info ) end
Find a status handler for the given status_code
and return it as an UnboundMethod.
# File lib/strelka/app/errors.rb, line 199 def status_handler_for( status_code ) self.log.debug "[:errors] Looking for a status handler for %d responses" % [ status_code ] handlers = self.class.status_handlers ranges = handlers.keys ranges.each do |range| return handlers[ range ] if range.include?( status_code ) end return nil end