flask 重写HTTPException类并自定义异常信息

前言

flask默认返回的异常是html格式的页面,但为了能更好的支持json的返回,所以我们得重写异常类的返回。
自定义返回符合resful风格的json,是重写了werkzeug.exceptions中的HTTPException异常类。

源码

在HTTPException类下第147-165行,是整个异常信息的返回格式,我们可以看得返回的类型是text/html
具体的返回值是在get_body方法中实现,所以我们需要重写两个函数方法,get_body和get_headers。

代码

目录结构

首先我们在项目的目录下新建一个libs目录,新建两个py文件,error.py和errorcode.py。

error.py文件是重写异常类的方法

首先我们得先继承HTTPException类作为父类

from werkzeug.exceptions import HTTPException



class APIException(HTTPException):
    """
    为了使代码简洁, 首先定义一个最基本的类, 供其它类继承, 这个自定义的APIException继承HTTPException.
    1. 为了返回特定的body信息, 需要重写get_body;
    2. 为了指定返回类型, 需要重写get_headers.
    3. 为了接收自定义的参数, 重写了__init__;
    4. 同时定义了类变量作为几个默认参数.(code500和error_code:999 均表示未知错误, error_code表示自定义异常code)
    """
    code = 500
    msg = 'sorry,internal error'
    error_code = 999
    data = ''
    
    # 自定义需要返回的信息,在初始化完成并交给父类
    def __init__(self, msg=None, code=None, error_code=None, data=None):
        if code:
            self.code = code
        if msg:
            self.msg = msg
        if error_code:
            self.error_code = error_code
        if data:
            self.data = data
        super(APIException, self).__init__(msg, None)

前面我们已经定义好了默认的错误响应码和返回消息,现在我们重写get_body和get_headers方法

    def get_body(self, environ=None):
        body = dict(
            error_code=self.error_code,
            msg=self.msg,
            request=request.method + ' ' + self.get_url_no_parm(),
            data=self.data
        )
        # sort_keys 取消排序规则,ensure_ascii 中文显示
        text = json.dumps(body, sort_keys=False, ensure_ascii=False)
        return text

    def get_headers(self, environ=None):
        return [('Content-Type', 'application/json')]

上面用到的self.get_url_no_parm方法是为了更好的知道我们访问出错的视图,我们从请求上下文中获取到路径展示。

from flask import request

def get_url_no_parm():
    full_path = str(request.path)
    return full_path

下面是整个APIException的完整代码

import json

from flask import request
from werkzeug.exceptions import HTTPException



class APIException(HTTPException):
    """
    为了使代码简洁, 首先定义一个最基本的类, 供其它类继承, 这个自定义的APIException继承HTTPException.
    1. 为了返回特定的body信息, 需要重写get_body;
    2. 为了指定返回类型, 需要重写get_headers.
    3. 为了接收自定义的参数, 重写了__init__;
    4. 同时定义了类变量作为几个默认参数.(code500和error_code:999 均表示未知错误, error_code表示自定义异常code)
    """
    code = 500
    msg = 'sorry,internal error'
    error_code = 999
    data = ''

    # 自定义需要返回的信息,在初始化完成并交给父类
    def __init__(self, msg=None, code=None, error_code=None, data=None):
        if code:
            self.code = code
        if msg:
            self.msg = msg
        if error_code:
            self.error_code = error_code
        if data:
            self.data = data
        super(APIException, self).__init__(msg, None)

    def get_body(self, environ=None):
        body = dict(
            error_code=self.error_code,
            msg=self.msg,
            request=request.method + ' ' + self.get_url_no_parm(),
            data=self.data
        )
        # sort_keys 取消排序规则,ensure_ascii 中文显示
        text = json.dumps(body, sort_keys=False, ensure_ascii=False)
        return text

    def get_headers(self, environ=None):
        return [('Content-Type', 'application/json')]

    @staticmethod
    def get_url_no_parm():
        full_path = str(request.path)
        return full_path


errorcode.py

我们定义的错误状态码,继承我们的APIException类,我们自定义的error_code表示的含义最好用文本形式记录下来,方面后期排错查看。

class ServerError(APIException):
    code = 500
    msg = "server is invallid"
    error_code = 999
    data = ''


class ClientTypeError(APIException):
    code = 400
    msg = "client is invallid"
    error_code = 1006
    data = ''


class ParameterException(APIException):
    code = 400
    msg = 'invalid parameter'
    error_code = 1000
    data = ''


class AuthFailed(APIException):
    code = 401
    msg = 'invalid parameter'
    error_code = 1001
    data = ''


class ValError(APIException):
    code = 404
    msg = 'invalid parameter'
    error_code = 1001
    data = ''

全局异常捕获

虽然我们前面定义好了自定义的异常类,但不知道应该如何让flask捕获到异常后,抛出我们自定义的异常信息。
flask内置好了一个装饰器方法,用于我们捕获异常并处理异常的方法app.errorhandler
这是程序上下文中的一个方法,所以我们需要放在app初始化后执行,在这里我们放到manage.py这个启动入口下。

······
# 创建flask应用对象
app = create_app('develop')
······

@app.errorhandler(Exception)
def framework_error(e):
    # 判断异常是不是APIException
    if isinstance(e, APIException):
        return e
    # 判断异常是不是HTTPException
    if isinstance(e, HTTPException):
        log.error(e)
        code = e.code
        # 获取具体的响应错误信息
        msg = e.description
        error_code = 1007
        return APIException(code=code, msg=msg, error_code=error_code)
    # 异常肯定是Exception
    else:
        # 如果是调试模式,则返回e的具体异常信息。否则返回json格式的ServerException对象!
        # 针对于异常信息,我们最好用日志的方式记录下来。
        if app.config["DEBUG"]:
            log.error(e)
            return e
        else:
            log.error(e)
            return ServerError()
posted @ 2020-05-25 10:28  吹神  阅读(2449)  评论(0编辑  收藏  举报