前言
flask 运行请求出现异常时,会先触发对应的异常钩子,比如出现404时,会根据NotFound 异常类返回404状态码。 我们也可以根据捕获异常钩子errorhandler 来自定义异常的输出。
404 NotFound
以404 NotFound 为例,在werkzeug.exceptions中可以找到
代码语言:javascript复制class NotFound(HTTPException):
"""*404* `Not Found`
Raise if a resource does not exist and never existed.
"""
code = 404
description = (
"The requested URL was not found on the server. If you entered"
" the URL manually please check your spelling and try again."
)
NotFound 类继承了一个基类HTTPException
代码语言:javascript复制class HTTPException(Exception):
"""The base class for all HTTP exceptions. This exception can be called as a WSGI
application to render a default error page or you can catch the subclasses
of it independently and render nicer error messages.
"""
code: t.Optional[int] = None
description: t.Optional[str] = None
我们只需要继承HTTPException类,自定义code和description就可以指定不同状态码返回了。
当我们访问一个不存在的地址,先抛出NotFound异常,然后触发异常钩子,返回对应的code和description
于是我们可以自定义这个404 页面
代码语言:javascript复制<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>404</title>
</head>
<body>
<h1>404 page object not found !</h1>
</body>
</html>
errorhandler 使用
errorhandler 相关源码
代码语言:javascript复制 @setupmethod
def errorhandler(
self, code_or_exception: t.Union[t.Type[Exception], int]
) -> t.Callable[[ErrorHandlerCallable], ErrorHandlerCallable]:
"""Register a function to handle errors by code or exception class.
A decorator that is used to register a function given an
error code. Example::
@app.errorhandler(404)
def page_not_found(error):
return 'This page does not exist', 404
You can also register handlers for arbitrary exceptions::
@app.errorhandler(DatabaseError)
def special_exception_handler(error):
return 'Database connection failed', 500
通过使用 errorhandler() 装饰函数来注册或者使用 register_error_handler() 来注册
代码语言:javascript复制@app.errorhandler(werkzeug.exceptions.BadRequest)
def handle_bad_request(e):
return 'bad request!', 400
# or, without the decorator
app.register_error_handler(400, handle_bad_request)
使用@app.errorhandler()
装饰器返回自定义的页面
from flask import Flask, request, g, abort, Response, render_template
app = Flask(__name__)
@app.errorhandler(404)
def error_404(error):
return render_template('404.html'), 404
@app.route("/demo", methods=["GET"])
def demo():
return {'msg': 'ok'}
if __name__ == '__main__':
app.run()
使用的时候需注意三点 1.errorhandler()括号里面传对应状态码或者一个异常类 2.函数error_404(error) 括号里面必须传一个位置参数接收异常 3.return 返回的时候需带上状态码(404),没带上状态码默认返回200
errorhandler 传异常类示例
从werkzeug.exceptions
导入异常类
from werkzeug.exceptions import NotFound
@app.errorhandler(NotFound)
def error_404(error):
return render_template('404.html'), 404
效果和上面传404 参数一样
自定义400 bad request
请求参数不合法时,我们一般会返回400 bad request, 默认返回的是一个html页面
在开发接口的时候,我们希望统一返回json 格式
代码语言:javascript复制@app.errorhandler(400)
def error_400(error):
return {'msg': '请求参数不合法', 'data': f'{error}'}, 400
@app.route("/demo", methods=["GET"])
def demo():
if request.args.get('username'):
abort(400)
return {'msg': 'ok'}
当访问一个不存在的地址时,就会返回400的json格式
代码语言:javascript复制HTTP/1.0 400 BAD REQUEST
Content-Type: application/json
Content-Length: 145
Server: Werkzeug/2.0.1 Python/3.8.5
Date: Sun, 11 Sep 2022 14:19:53 GMT
{
"data": "400 Bad Request: The browser (or proxy) sent a request that this server could not understand.",
"msg": "请求参数不合法"
}
处理
在处理请求时,当 Flask 捕捉到一个异常时,它首先根据代码检索。如果该代码没 有注册处理器,它会根据类的继承来查找,确定最合适的注册处理器。如果找不到已 注册的处理器,那么 HTTPException 子类会显示 一个关于代码的通用消息。没有代码的异常会被转化为一个通用的 500 内部服务器 错误。
例如,如果一个 ConnectionRefusedError 的实例被抛出,并且一个出错处 理器注册到 ConnectionError 和 ConnectionRefusedError ,那么 会使用更合适的 ConnectionRefusedError 来处理异常实例,生成响应。
当一个蓝图在处理抛出异常的请求时,在蓝图中注册的出错处理器优先于在应用中全 局注册的出错处理器。 但是,蓝图无法处理 404 路由错误,因为 404 发生的路由级 别还不能检测到蓝图。
通用异常处理器
可以为非常通用的基类注册异常处理器,例如 HTTPException 基类或者甚至 Exception 基类。但是,请注意,这样会捕捉到超出你预期的异常。
基于 HTTPException 的异常处理器对于把缺省的 HTML 出错页面转换为 JSON 非常有用,但是这个处理器会触发不由你直接产生的东西, 如路由过程中产生的 404 和 405 错误。请仔细制作你的处理器,确保不会丢失关于 HTTP 错误的信息。
代码语言:javascript复制from flask import json
from werkzeug.exceptions import HTTPException
@app.errorhandler(HTTPException)
def handle_exception(e):
"""Return JSON instead of HTML for HTTP errors."""
# start with the correct headers and status code from the error
response = e.get_response()
# replace the body with JSON
response.data = json.dumps({
"code": e.code,
"name": e.name,
"description": e.description,
})
response.content_type = "application/json"
return response
基于 Exception 的异常处理器有助于改变所有异常处理的表现形式,甚至包含 未处理的异常。但是,与在 Python 使用 except Exception: 类似,这样会捕 获 所有 未处理的异常,包括所有 HTTP 状态码。因此,在大多数情况下,设定 只针对特定异常的处理器比较安全。因为 HTTPException 实例是一个合法的 WSGI 响应,你可以直接传递该实例。
代码语言:javascript复制from werkzeug.exceptions import HTTPException
@app.errorhandler(Exception)
def handle_exception(e):
# pass through HTTP errors
if isinstance(e, HTTPException):
return e
# now you're handling non-HTTP exceptions only
return render_template("500_generic.html", e=e), 500
异常处理器仍然遵循异常烦类的继承层次。如果同时基于 HTTPException 和 Exception 注册了异常处理器, Exception 处理器不会处理 HTTPException 子类,因为 HTTPException 更有针对性。
未处理的异常 500
当一个异常发生时,如果没有对应的异常处理器,那么就会返回一个 500 内部服务错误。关于此行为的更多内容参见 flask.Flask.handle_exception() 。
如果针为 InternalServerError 注册了异常处理器,那么出现内部服务错误时就 会调用这个处理器。自 Flask 1.1.0 开始,总是会传递一个 InternalServerError 实例给这个异常处理器,而不是以前的未处理异常。原始 的异常可以通过 e.original_error 访问。在 Werkzeug 1.0.0 以前,这个属性 只有未处理异常有。建议使用 getattr 访问这个属性,以保证兼容性。
代码语言:javascript复制@app.errorhandler(InternalServerError)
def handle_500(e):
original = getattr(e, "original_exception", None)
if original is None:
# direct 500 error, such as abort(500)
return render_template("500.html"), 500
# wrapped unhandled error
return render_template("500_unhandled.html", e=original), 500
2022年第 12期《python接口web自动化 测试开发》课程,9月17号开学!
本期上课时间:2022年9月17号 - 2022年12月17号,周六周日上午9:00-11:00