[Proposal] Allow for registration of custom error handlers
- Dominant language
- Python
- Stars
- 11.1k
- Forks
- 1k
- Avg merge
- 1d 22h
- Merged PRs (30d)
- 2
Description
I first put my proposal in response to #1884. But in an effort to make sure the work I do is in a desired direction, I am copying my comments here so that it can be seen and hopefully handled.
# Problem
While the `ChaliceUnhandledError` works fine for custom errors, third party libraries can raise Exceptions that can't be subclassed. Having a `try/except` in a middleware won't successfully catch the Exception as it is already a `Response` at that point.
## Goals
- Exceptions on REST APIs must be intercepted before the `InternalServerError` response is built.
- Must have knowledge of the context of the raised exception.
- Must be able to return a `Response`
- An invalid `Response` or an exception raised in the handler should still return the standard `InternalServerError` and have the stack trace replaced by the expected json object when not in debug mode
- As existing middlewares can already successfully intercept all exceptions for other events, and `ChaliceUnhandledError` only exists in the scope of `http` event, so will the focus of this handler.
# Specification
A new `@app.errorhandler` is to be added. The decorated function will take one arg, the exception.
Similar to the way middlewares can be registered, we will be able to register the error handler in app or in a blueprint with either a decorator or `app.register_error`.
```
# Decorator
@app.error(MyCustomError)
def my_handler(e: MyCustomError):
return Response(body="", status_code:400)
# Register
app.register_error_handler(MyCustomError, my_handler)
# Blueprint
@my_blueprint.error(MyCustomError)
def my_handler(e: MyCustomError):
return Response(body="", status_code:400)
In the examples above, any http endpoint raising MyCustomError will then return a 400 with no body.
```
If, however, the return value isn't a `Response` or an exception was raised during execution of the handler, the normal flow of the `RestAPIEventHandler` will be maintained.
```
@app.error(MyCustomError)
def my_handler(e: MyCustomError):
return "Not a Response object"
```
Will return
```
{
"Code": "InternalServerError",
"Message": "An internal server error occurred."
}
```
Contributor guide
Research direction
Start with RestAPIEventHandler, ChaliceUnhandledError, and the existing middleware registration paths. Trace how REST exceptions become InternalServerError responses, then verify that registered app and blueprint handlers receive the exception, can return a Response, and preserve the standard error response for invalid results or handler failures.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- api, backend
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Clearly specified
- Newbie friendliness
- 35/100