Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ability to add handlers for raised exceptions #688

Open
wants to merge 8 commits into
base: master
Choose a base branch
from

Conversation

syeopite
Copy link

Closes #622

Description of the Change

This PR adds the ability for users to define error handlers that are used when a specific exception is raised.

require "kemal"

class NewException < Exception
end

get "/" do | env |
  raise NewException.new()
end

error NewException do | env |
  "An error occured!"
end

Kemal.run

Alternate Designs

Benefits

This provides for a cleaner and easier experience for defining custom errors. See #622.

Possible Drawbacks

@@ -11,12 +11,26 @@ module Kemal
rescue ex : Kemal::Exceptions::CustomException
call_exception_with_status_code(context, ex, context.response.status_code)
rescue ex : Exception
# Use error handler defined for the current exception if it exists
return call_exception_with_exception(context, ex, 500) if Kemal.config.error_handlers.has_key?(ex.class)
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line is added above the log function in order for the specs to pass.

A logger is needed for #log to be able to successfully run which the tests in exception_handler_spec.cr doesn't appear to have defined.

This shouldn't be a problem in the real world case as a logger would always exist afaik.

Please let me know if this should be rectified in some other way.

@robacarp
Copy link

This is perfect!

src/kemal/exception_handler.cr Outdated Show resolved Hide resolved
Co-authored-by: Johannes Müller <straightshoota@gmail.com>
@syeopite syeopite force-pushed the handle-custom-errors branch from e7f441a to bf19e27 Compare September 20, 2024 02:36
src/kemal/exception_handler.cr Outdated Show resolved Hide resolved
src/kemal/config.cr Outdated Show resolved Hide resolved
src/kemal/exception_handler.cr Outdated Show resolved Hide resolved
`ex.class <= key` below should already handle the exact match

```crystal
Kemal.config.error_handlers.each_key do |key|
  if ex.class <= key
  end
end

```
Prior to this commit, handlers for exceptions and handlers for
HTTP status codes were stored in a single collection. However,
the two are used completely independently from each other and
should instead be stored in two separate collections.

# Adds an error handler for the given exception
def add_exception_handler(exception : Exception.class, &handler : HTTP::Server::Context, Exception -> _)
EXCEPTION_HANDLERS[exception] = ->(context : HTTP::Server::Context, error : Exception) { handler.call(context, error).to_s }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this can be added as given, avoiding an additional closure.

Suggested change
EXCEPTION_HANDLERS[exception] = ->(context : HTTP::Server::Context, error : Exception) { handler.call(context, error).to_s }
EXCEPTION_HANDLERS[exception] = handler

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I agree requiring String output type should be preferred in general. This might have some UX consequences though...
@syeopite What's the motivation for an unspecified block output type?

Copy link
Member

@sdogruyol sdogruyol left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM @syeopite, thanks a lot 👍 Waiting for the review of @straight-shoota

def add_error_handler(status_code : Int32, &handler : HTTP::Server::Context, Exception -> _)
ERROR_HANDLERS[status_code] = ->(context : HTTP::Server::Context, error : Exception) { handler.call(context, error).to_s }
end

# Returns tje defined error handlers for exceptions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo:

Suggested change
# Returns tje defined error handlers for exceptions
# Returns the defined error handlers for exceptions


# Adds an error handler for the given exception
def add_exception_handler(exception : Exception.class, &handler : HTTP::Server::Context, Exception -> _)
EXCEPTION_HANDLERS[exception] = ->(context : HTTP::Server::Context, error : Exception) { handler.call(context, error).to_s }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I agree requiring String output type should be preferred in general. This might have some UX consequences though...
@syeopite What's the motivation for an unspecified block output type?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Allow handling errors with error handlers
5 participants