# 异常处理(Exceptions)
# 使用 Sanic 内置异常(Using Sanic exceptions)
有时,您只需要告诉 Sanic 终止执行响应函数,并返回一个状态码。此时,您可以抛出 SanicException
异常,之后,Sanic 将为您自动完成剩下的工作。
您可以选择传递一个参数 status_code
。默认情况下,如果您不传递该参数,SanicException 将会返回一个 HTTP 500 内部服务错误的响应
from sanic.exceptions import SanicException
@app.route("/youshallnotpass")
async def no_no(request):
raise SanicException("Something went wrong.", status_code=501)
Sanic 预置了许多标准异常。它们每个都会在您的响应中自动触发适当的 HTTP 状态代码。查看 接口文档 (opens new window) 了解更多详细信息。
您应该自己实现的更常见的异常包括:
InvalidUsage
(400)Unauthorized
(401)Forbidden
(403)NotFound
(404)ServerError
(500)
from sanic import exceptions
@app.route("/login")
async def login(request):
user = await some_login_func(request)
if not user:
raise exceptions.NotFound(
f"Could not find user with username={request.json.username}"
)
...
# 异常属性(Exception properties)
在 Sanic 中,所有异常都继承自 SanicException
。该类有一些属性,可以帮助开发人员在整个应用程序中一致地报告异常。
message
status_code
quiet
context
extra
所有这些属性都可以在异常创建时传递给它,但是前三个将会作为可见的信息被返回出来。
# 信息属性(message
)
message
属性控制着 Python 中任何其他异常显示的消息。您可以在类定义上设置 message
属性,以便在整个应用程序中轻松实现语言的标准化。
class CustomError(SanicException):
message = "Something bad happened"
raise CustomError
# or
raise CustomError("Override the default message with something else")
# 状态码属性(status_code
)
该属性用于设置异常时的响应码。这在创建自定义 400 系列异常时特别有用,这些异常通常是为了响应来自客户端的错误信息。
class TeapotError(SanicException):
status_code = 418
message = "Sorry, I cannot brew coffee"
raise TeapotError
# or
raise TeapotError(status_code=400)
# 静默输出(quiet
)
默认情况下,Sanic 会将异常输出到 error_logger
。有时这可能并不理想,尤其是当您使用异常来触发异常处理程序中的事件时(参见下一节)。您可以使用quiet=True
来禁止日志输出。
class SilentError(SanicException):
message = "Something happened, but not shown in logs"
quiet = True
raise SilentError
# or
raise InvalidUsage("blah blah", quiet=True)
有时在调试时,您可能希望全局忽略 quiet=True
属性。您可以使用 NOISY_EXCEPTIONS
来忽略 quiet
的设置,强制 Sanic 注销所有异常。
app.config.NOISY_EXCEPTIONS = True
# 处理(Handling)
Sanic 通过呈现错误页面来自动处理异常,因此在许多情况下,您不需要自己处理它们。但是,如果您希望在引发异常时更多地控制该做什么,您同样可以自己实现一个处理程序。
Sanic 为此提供了一个装饰器,它不仅适用于 Sanic 标准异常,还适用于您的应用程序可能抛出的任何异常。
添加处理程序最简单的方法是使用 @app.exception()
并向其传递一个或多个异常。
from sanic.exceptions import NotFound
@app.exception(NotFound, SomeCustomException)
async def ignore_404s(request, exception):
return text("Yep, I totally found the page: {}".format(request.url))
您也可以通过捕获 Exception
来创建一个异常捕获处理程序。
@app.exception(Exception)
async def catch_anything(request, exception):
...
您也可以使用 app.error_handler.add()
来添加异常处理程序。
async def server_error_handler(request, exception):
return text("Oops, server error", status=500)
app.error_handler.add(Exception, server_error_handler)
# 内置异常处理(Built-in error handling)
Sanic 支持三种不同的异常格式: HTML、JSON 和 TEXT。您可以在下面的 异常格式 一节中看到它们的示例。
您可以通过设置 error_format
关键字参数来控制每一个路由所使用的异常格式。
@app.request("/", error_format="text")
async def handler(request):
...
# 自定义异常处理(Custom error handling)
在某些情况下,您可能希望在默认设置的基础上增加更多的错误处理功能。在这种情况下,您可以将 Sanic 的默认错误处理程序子类化,例如:
from sanic.handlers import ErrorHandler
class CustomErrorHandler(ErrorHandler):
def default(self, request, exception):
''' handles errors that have no error handlers assigned '''
# You custom error handling logic...
return super().default(request, exception)
app.error_handler = CustomErrorHandler()
# 异常格式(Fallback handler)
Sanic comes with three fallback exception handlers: Sanic 自带了三种异常格式。
- HTML (default)
- Text
- JSON
根据您的应用程序是否处于 调试模式,这些异常内容将呈现不同级别的细节。
# HTML
app.config.FALLBACK_ERROR_FORMAT = "html"
app.config.DEBUG = True
app.config.DEBUG = False
# Text
app.config.FALLBACK_ERROR_FORMAT = "text"
app.config.DEBUG = True
$ curl localhost:8000/exc -i
HTTP/1.1 500 Internal Server Error
content-length: 590
connection: keep-alive
content-type: text/plain; charset=utf-8
⚠️ 500 — Internal Server Error
==============================
That time when that thing broke that other thing? That happened.
ServerError: That time when that thing broke that other thing? That happened. while handling path /exc
Traceback of __BASE__ (most recent call last):
ServerError: That time when that thing broke that other thing? That happened.
File /path/to/sanic/app.py, line 986, in handle_request
response = await response
File /path/to/server.py, line 222, in exc
raise ServerError(
app.config.DEBUG = False
$ curl localhost:8000/exc -i
HTTP/1.1 500 Internal Server Error
content-length: 134
connection: keep-alive
content-type: text/plain; charset=utf-8
⚠️ 500 — Internal Server Error
==============================
That time when that thing broke that other thing? That happened.
# JSON
app.config.FALLBACK_ERROR_FORMAT = "json"
app.config.DEBUG = True
$ curl localhost:8000/exc -i
HTTP/1.1 500 Internal Server Error
content-length: 129
connection: keep-alive
content-type: application/json
{
"description": "Internal Server Error",
"status": 500,
"message": "That time when that thing broke that other thing? That happened.",
"path": "/exc",
"args": {},
"exceptions": [
{
"type": "ServerError",
"exception": "That time when that thing broke that other thing? That happened.",
"frames": [
{
"file": "/path/to/sanic/app.py",
"line": 986,
"name": "handle_request",
"src": "response = await response"
},
{
"file": "/path/to/server.py",
"line": 222,
"name": "exc",
"src": "raise ServerError("
}
]
}
]
}
app.config.DEBUG = False
$ curl localhost:8000/exc -i
HTTP/1.1 500 Internal Server Error
content-length: 530
connection: keep-alive
content-type: application/json
{
"description": "Internal Server Error",
"status": 500,
"message": "That time when that thing broke that other thing? That happened."
}
# Auto
Sanic 还提供了一个选项,用于猜测使用哪种异常格式。该功能依旧处于 试验阶段。
app.config.FALLBACK_ERROR_FORMAT = "auto"
# 上下文异常(Contextual Exceptions)
默认异常消息简化了在整个应用程序中一致引发异常的能力。
class TeapotError(SanicException):
status_code = 418
message = "Sorry, I cannot brew coffee"
raise TeapotError
但这样会忽视两个非常重要的问题:
- 如何设置动态且可预测的消息格式
- 如何向错误消息中添加额外上下文内容(稍后将详细介绍)
# 使用 extra
来设置动态且可预测的消息(Dynamic and predictable message using extra
)
Sanic 异常可以使用 extra
属性来获取传入的额外信息,例如:
class TeapotError(SanicException):
status_code = 418
@property
def message(self):
return f"Sorry {self.extra['name']}, I cannot make you coffee"
raise TeapotError(extra={"name": "Adam"})
这个新特性允许将 extra
元传递给异常实例,这在上面的例子中将动态数据传递给消息文本时非常有用。在生产模式下,extra
信息对象 将被隐藏,但在开发模式下将s显示。
生产模式
开发模式
# 在异常信息中附加上下文内容(Additional context
to an error message)
Sanic 异常也可以通过 context
参数来引发,方便告诉开发者发生了什么。这对微服务或旨在以 JSON 格式传递错误消息的 API 特别有用。在这个用例中,我们希望有一些关于异常的上下文,而不仅仅是一个可解析的错误消息,以便向客户端返回详细信息。
raise TeapotError(context={"foo": "bar"})
context
将始终被传递到异常信息中,这正是我们想要看到的,它看起来就像这样:
生产模式
{
"description": "I'm a teapot",
"status": 418,
"message": "Sorry Adam, I cannot make you coffee",
"context": {
"foo": "bar"
}
}
开发模式
{
"description": "I'm a teapot",
"status": 418,
"message": "Sorry Adam, I cannot make you coffee",
"context": {
"foo": "bar"
},
"path": "/",
"args": {},
"exceptions": [
{
"type": "TeapotError",
"exception": "Sorry Adam, I cannot make you coffee",
"frames": [
{
"file": "handle_request",
"line": 83,
"name": "handle_request",
"src": ""
},
{
"file": "/tmp/p.py",
"line": 17,
"name": "handler",
"src": "raise TeapotError("
}
]
}
]
}