Flask 全局异常处理

文章目录

  • 一、异常处理流程
    • 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 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)

一、异常处理流程

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 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 是捕获不了的,只能单独处理。

参考资料

  • flask 全局异常处理:https://blog.csdn.net/yy_diego/article/details/127775426
  • Python Web异常处理:https://blog.csdn.net/aizaiyishunjian/article/details/129892499

你可能感兴趣的:(flask,python,后端)