commit
4b80ffb9eb
|
@ -17,6 +17,19 @@ def i_am_ready_to_die(request):
|
|||
raise ServerError("Something bad happened", status_code=500)
|
||||
```
|
||||
|
||||
You can also use the `abort` function with the appropriate status code:
|
||||
|
||||
```python
|
||||
from sanic.exceptions import abort
|
||||
from sanic.response import text
|
||||
|
||||
@app.route('/youshallnotpass')
|
||||
def no_no(request):
|
||||
abort(401)
|
||||
# this won't happen
|
||||
text("OK")
|
||||
```
|
||||
|
||||
## Handling exceptions
|
||||
|
||||
To override Sanic's default handling of an exception, the `@app.exception`
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
from sanic.response import ALL_STATUS_CODES, COMMON_STATUS_CODES
|
||||
|
||||
TRACEBACK_STYLE = '''
|
||||
<style>
|
||||
body {
|
||||
|
@ -115,6 +117,20 @@ INTERNAL_SERVER_ERROR_HTML = '''
|
|||
'''
|
||||
|
||||
|
||||
_sanic_exceptions = {}
|
||||
|
||||
|
||||
def add_status_code(code):
|
||||
"""
|
||||
Decorator used for adding exceptions to _sanic_exceptions.
|
||||
"""
|
||||
def class_decorator(cls):
|
||||
cls.status_code = code
|
||||
_sanic_exceptions[code] = cls
|
||||
return cls
|
||||
return class_decorator
|
||||
|
||||
|
||||
class SanicException(Exception):
|
||||
|
||||
def __init__(self, message, status_code=None):
|
||||
|
@ -124,24 +140,27 @@ class SanicException(Exception):
|
|||
self.status_code = status_code
|
||||
|
||||
|
||||
@add_status_code(404)
|
||||
class NotFound(SanicException):
|
||||
status_code = 404
|
||||
pass
|
||||
|
||||
|
||||
@add_status_code(400)
|
||||
class InvalidUsage(SanicException):
|
||||
status_code = 400
|
||||
pass
|
||||
|
||||
|
||||
@add_status_code(500)
|
||||
class ServerError(SanicException):
|
||||
status_code = 500
|
||||
pass
|
||||
|
||||
|
||||
class URLBuildError(SanicException):
|
||||
status_code = 500
|
||||
class URLBuildError(ServerError):
|
||||
pass
|
||||
|
||||
|
||||
class FileNotFound(NotFound):
|
||||
status_code = 404
|
||||
pass
|
||||
|
||||
def __init__(self, message, path, relative_url):
|
||||
super().__init__(message)
|
||||
|
@ -149,20 +168,23 @@ class FileNotFound(NotFound):
|
|||
self.relative_url = relative_url
|
||||
|
||||
|
||||
@add_status_code(408)
|
||||
class RequestTimeout(SanicException):
|
||||
status_code = 408
|
||||
pass
|
||||
|
||||
|
||||
@add_status_code(413)
|
||||
class PayloadTooLarge(SanicException):
|
||||
status_code = 413
|
||||
pass
|
||||
|
||||
|
||||
class HeaderNotFound(SanicException):
|
||||
status_code = 400
|
||||
class HeaderNotFound(InvalidUsage):
|
||||
pass
|
||||
|
||||
|
||||
@add_status_code(416)
|
||||
class ContentRangeError(SanicException):
|
||||
status_code = 416
|
||||
pass
|
||||
|
||||
def __init__(self, message, content_range):
|
||||
super().__init__(message)
|
||||
|
@ -174,3 +196,20 @@ class ContentRangeError(SanicException):
|
|||
|
||||
class InvalidRangeType(ContentRangeError):
|
||||
pass
|
||||
|
||||
|
||||
def abort(status_code, message=None):
|
||||
"""
|
||||
Raise an exception based on SanicException. Returns the HTTP response
|
||||
message appropriate for the given status code, unless provided.
|
||||
:param status_code: The HTTP status code to return.
|
||||
:param message: The HTTP response body. Defaults to the messages
|
||||
in response.py for the given status code.
|
||||
"""
|
||||
if message is None:
|
||||
message = COMMON_STATUS_CODES.get(status_code,
|
||||
ALL_STATUS_CODES.get(status_code))
|
||||
# These are stored as bytes in the STATUS_CODES dict
|
||||
message = message.decode('utf8')
|
||||
sanic_exception = _sanic_exceptions.get(status_code, SanicException)
|
||||
raise sanic_exception(message=message, status_code=status_code)
|
||||
|
|
|
@ -3,7 +3,7 @@ from bs4 import BeautifulSoup
|
|||
|
||||
from sanic import Sanic
|
||||
from sanic.response import text
|
||||
from sanic.exceptions import InvalidUsage, ServerError, NotFound
|
||||
from sanic.exceptions import InvalidUsage, ServerError, NotFound, abort
|
||||
|
||||
|
||||
class SanicExceptionTestException(Exception):
|
||||
|
@ -30,6 +30,11 @@ def exception_app():
|
|||
def handler_invalid(request):
|
||||
raise InvalidUsage("OK")
|
||||
|
||||
@app.route('/abort')
|
||||
def handler_invalid(request):
|
||||
abort(500)
|
||||
return text("OK")
|
||||
|
||||
@app.route('/divide_by_zero')
|
||||
def handle_unhandled_exception(request):
|
||||
1 / 0
|
||||
|
@ -60,6 +65,7 @@ def test_catch_exception_list():
|
|||
request, response = app.test_client.get('/')
|
||||
assert response.text == 'ok'
|
||||
|
||||
|
||||
def test_no_exception(exception_app):
|
||||
"""Test that a route works without an exception"""
|
||||
request, response = exception_app.test_client.get('/')
|
||||
|
@ -97,6 +103,7 @@ def test_handled_unhandled_exception(exception_app):
|
|||
"The server encountered an internal error and "
|
||||
"cannot complete your request.")
|
||||
|
||||
|
||||
def test_exception_in_exception_handler(exception_app):
|
||||
"""Test that an exception thrown in an error handler is handled"""
|
||||
request, response = exception_app.test_client.get(
|
||||
|
@ -121,3 +128,9 @@ def test_exception_in_exception_handler_debug_off(exception_app):
|
|||
debug=True)
|
||||
assert response.status == 500
|
||||
assert response.body.startswith(b'Exception raised in exception ')
|
||||
|
||||
|
||||
def test_abort(exception_app):
|
||||
"""Test the abort function"""
|
||||
request, response = exception_app.test_client.get('/abort')
|
||||
assert response.status == 500
|
||||
|
|
Loading…
Reference in New Issue
Block a user