文章目录
- 一、异常处理流程
- 1.1 异常注册
- 1.1.1 装饰器模式
- 1.1.2 工厂模式
- 1.2 异常触发
- 1.2.1 assert触发异常
- 1.2.2 raise触发异常
- 1.2.3 abort触发异常
- 1.3 异常处理
- 1.3.1 正常请求
- 1.3.2 异常请求
- 二、自定义异常(客户端异常)
- 三、服务端异常
- 参考资料
异常分为客户端异常和服务端异常,flask 中的异常处理分为三步走:异常注册、异常触发、异常处理。
关于客户端/服务端异常,先看段样例代码:
from flask import *
from paddlenlp import Taskflow
from werkzeug.exceptions import HTTPExceptionapp = Flask(__name__)
app.config['JSON_AS_ASCII'] = False@app.errorhandler(Exception)
def handle_500_exception(e):# pass through HTTP errorsif isinstance(e, HTTPException):return e# now you're handling non-HTTP exceptions onlyreturn 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 responseclass RequestParamException(HTTPException):code = 400def 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}, 201if __name__ == '__main__':app.register_error_handler(RequestParamException, handle_param_exception)app.run(debug=True)
一、异常处理流程
1.1 异常注册
常用的异常注册的模式包括两种:
- 装饰器模式:
@app.errorhandler
- 工厂模式:
app.register_error_handler()
1.1.1 装饰器模式
使用装饰器模式注册异常时,针对Exception异常(服务端异常)和HttpException异常(客户端异常),注册的时候可以指定具体处理的异常类型,也可以统一处理。
服务端异常注册:
@app.errorhandler(Exception)
def handle_error(e):"""Handle errors, formatting them as JSON if requested"""......
客户端异常注册:
@app.errorhandler(HTTPException)
def handle_error(e):"""Handle errors, formatting them as JSON if requested"""......
1.1.2 工厂模式
服务端异常注册:
app.register_error_handler(Exception, handle_error)
客户端异常注册:
app.register_error_handler(HTTPException, handle_error)
1.2 异常触发
异常触发的方式通常有以下几种:
1.2.1 assert触发异常
assert语句又称作断言,指的是程序期望满足指定的条件。定义的约束条件不满足的时候,它会触发AssertionError异常,所以assert语句可以当作条件式的raise语句。
assert 逻辑表达式, data
data是可选的,通常是一个字符串,当表达式的结果为False时,作为异常类型的描述信息使用。
assert语句等同于:
if not 逻辑表达式:raise AssertionError(data)
1.2.2 raise触发异常
raise [exceptionName [(reason)]]
其中,用 [] 括起来的为可选参数,其作用是指定抛出的异常名称,以及异常信息的相关描述。如果可选参数全部省略,则 raise 会把当前错误原样抛出;如果之前没有触发异常,触发RuntimeError;如果仅省略 (reason),则在抛出异常时,将不附带任何的异常描述信息。
- raise
- raise exceptionName
- raise exceptionName(reason)
1.2.3 abort触发异常
在视图函数执行过程中,我们可以使用abort函数立即终止视图函数的执行。通过abort函数,可以返回一个app.aborter中存在的错误状态码,表示出现的错误信息。类似于python中raise。
abort(404)
1.3 异常处理
在上述客户端全局异常捕获过程中,异常的处理方法为 handle_exception(e)
。
客户端的所有请求都囊括了,404、405、400 等等,一旦这些异常触发,都会执行到 handle_exception(e) 方法中,最后将异常信息返回给客户端。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
1.3.1 正常请求
请求内容:
curl --request POST \--url http://127.0.0.1:5000/add_user \--header 'Content-Type: application/json' \--data '{"user":"diego"}'
返回结果:
{"user": "diego"
}
1.3.2 异常请求
(1)触发 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"
}
(2) 触发 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 基础上自定义异常信息,比如在参数不符合要求,抛出异常。当子类异常与父类异常同时存在时,优先让子类异常捕获。
注意:不要直接触发HttpException异常,这样会导致错误不能被正常捕获并处理。
在Flask的异常处理流程中,首先会单独判断一下是否HTTPException异常,如果是的话转入handle_http_exeption处理器进行处理。然后handle_http_exeption处理逻辑中,会默认访问code属性。但是直接触发HTTPException时是无法设置code属性的。因此这时会引发新的异常,导致错误处理不会走我们注册的处理程序,造成莫名其妙的错误。
这里,建议我们要么触发类似NotFound这些基于HTTPException扩展的异常,要么可以基于HTTPException继承自定义的异常。
(1)定义异常
## 定义异常类
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
(2)注册异常
这里,在app.py文件的main函数中,使用工厂模式注册RequestParamException异常:
if __name__ == '__main__':app.register_error_handler(RequestParamException, handle_param_exception)app.run()
(3)异常触发
请求内容:
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 errorsif isinstance(e, HTTPException):return e# now you're handling non-HTTP exceptions onlyreturn render_template("500_generic.html", e=e), 500
服务端异常 HTTPException 是捕获不了的,只能单独处理。
参考资料
- flask 全局异常处理:https://blog.csdn.net/yy_diego/article/details/127775426
- Python Web异常处理:https://blog.csdn.net/aizaiyishunjian/article/details/129892499