前言
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(temp.format(info.f_code.co_filename, info.f_lineno, name, repr(e)))
log.error(e)
return e
else:
log.error(e)
return ServerError()