场景描述
这几天刚接到的需求:padlepadle + flask + docker。web功能不复杂,但该有的日志、异常捕获这些都还是不能少的。之前异常捕获老用 try-except,这次想试试全局异常捕获,花了几个小时研究了下。发现关键的就几行代码。
全局异常捕获
异常分为客户端异常和服务端异常,flask 中的异常处理分为三步走:异常注册、异常触发、异常处理。先看段代码,边测边写。
from flask import *
from paddlenlp import Taskflow
from werkzeug.exceptions import HTTPException
app = Flask(__name__)
app.config['JSON_AS_ASCII'] = False
@app.errorhandler(Exception)
def handle_500_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
@app.errorhandler(HTTPException)
def handle_exception(e):
response = e.get_response()
response.data = json.dumps({
"code": e.code,
"name": e.name,
"description": e.description,
})
response.content_type = "application/json"
return response
class RequestParamException(HTTPException):
code = 400
def handle_param_exception(e):
response = e.get_response()
response.data = json.dumps({
"code": e.code,
"name": "子类:RequestParamException 处理的异常。",
"description": e.description,
})
response.content_type = "application/json"
return response
@app.route('/add_user', methods=['POST'])
def add_documents():
data = request.get_json()
user = data['user']
if user is None or len(user) <= 0:
raise RequestParamException(description="user 不能为 None 或 ''。")
return {
"user": user}, 201
if __name__ == '__main__':
app.register_error_handler(RequestParamException, handle_param_exception)
app.run(debug=True)
正常请求
curl --request POST \
--url http://127.0.0.1:5000/add_user \
--header 'Content-Type: application/json' \
--data '{"user":"diego"}'
#----
{
"user": "diego"
}
客户端全局异常捕获,@app.errorhandler 向 flask 注册了 HTTPException
异常以及该异常的处理方法 handle_exception(e)
。客户端的所有请求他都囊括了,404、405、400 等等,一旦这些异常触发,都会执行到 handle_exception(e) 方法中,最后将异常信息返回给客户端。至于方法里面的具体内容,自己看吧。
@app.errorhandler(HTTPException)
def handle_exception(e):
response = e.get_response()
response.data = json.dumps({
"code": e.code,
"name": e.name,
"description": e.description,
})
response.content_type = "application/json"
return response
触发 404 异常
curl --request POST \
--url http://127.0.0.1:5000/add_user2 \
--header 'Content-Type: application/json' \
--data '{"user":"diego"}' | jq
{
"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.",
"name": "Not Found"
}
触发 405 异常
curl --request GET \
--url http://127.0.0.1:5000/add_user \
--header 'Content-Type: application/json' \
--data '{"user":"diego"}' | jq
{
"code": 405,
"description": "The method is not allowed for the requested URL.",
"name": "Method Not Allowed"
}
自定义异常
还可以在 HTTPException 基础上自定义异常信息,比如在参数不符合要求,抛出异常。当子类异常与父类异常同时存在时,优先让子类异常捕获。
## 定义异常类
class RequestParamException(HTTPException):
code = 400
## 定义异常处理方法
def handle_param_exception(e):
response = e.get_response()
response.data = json.dumps({
"code": e.code,
"name": "子类:RequestParamException 处理的异常。",
"description": e.description,
})
response.content_type = "application/json"
return response
注册异常除了 @app.errorhandler 以外,还有另外的一种方式:
app.register_error_handler(RequestParamException, handle_param_exception)
测试参数异常
curl --request POST \
--url http://127.0.0.1:5000/add_user \
--header 'Content-Type: application/json' \
--data '{"user":""}' | jq
{
"code": 400,
"description": "user 不能为 None 或 ''。",
"name": "子类:RequestParamException 处理的异常。"
}
服务端异常
@app.errorhandler(Exception)
def handle_500_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 是捕获不了的,只能单独处理。
处理特定状态码的异常
@app.errorhandler(406)
def handle_exception(e):
response = e.get_response()
response.data = json.dumps({
"code": e.code,
"name": e.name,
"description": e.description,
})
response.content_type = "application/json"
return response