flask: 內置HttpException的rest风格改进
在api的设计中, 无论异常还是正常数据均需要服务器以json的格式返回, 为了对异常的统一管理, 同时为了后续更加方便的返回和验证数据, 我们自定义异常返回类. 设计异常数据的返回格式为:
{
"error_code": 999,
"msg": "sorry, we make a mistake",
"request": "POST /v1/client/register"
}
异常值分别代表:
999 未知错误
1006 客户端错误
1007 服务器错误
1000 参数错误
...
其中json中的request
包含了请求的方法和路径.
一. 定义一个最基本的类
二. 参数错误和客户端错误类
三. 所有的异常
一. 定义一个最基本的类
为了使代码简洁, 首先定义一个最基本的类, 供其它类继承, 这个自定义的APIException继承HTTPException. 1. 为了返回特定的body信息, 需要重写get_body; 2. 为了指定返回类型, 需要重写get_headers. 3. 为了接收自定义的参数, 重写了__init__; 4. 同时定义了类变量作为几个默认参数.(code500和error_code:999 均表示未知错误, error_code表示自定义异常code)
from flask import request, json
from werkzeug.exceptions import HTTPException
class ApiException(HTTPException):
code = 500
msg = 'sorry, we make a mistake'
error_code = 999
def __init__(self, code=None, msg=None, error_code=None, header=None):
if code:
self.code = code
if msg:
self.msg = msg
if error_code:
self.error_code = error_code
super(ApiException, self).__init__(msg, None)
def get_body(self, environ=None):
body = dict(
msg=self.msg,
error_code=self.error_code,
# 形如request="POST v1/client/register"
request=request.method + ' ' + self.get_url_no_param()
)
text = json.dumps(body)
return text
def get_headers(self, environ=None):
return [('Content-Type', 'application/json')]
@staticmethod
def get_url_no_param():
full_url = str(request.full_path)
main_path = full_url.split('?')
return main_path[0]
二. 参数错误和客户端错误类
这两个类继承ApiException, 只是重写了三个默认的类变量.
class ClientTypeError(ApiException):
code = 400
msg = 'client is invalid'
error_code = 1006
# 参数错误
class ParameterException(ApiException):
code = 400
msg = 'invalid parameter'
error_code = 1000
一切定义完毕! 那么该怎么如何抛出异常呢?
# 抛出参数异常
raise ParameterException(msg=self.errors)
当然也可以只捕捉异常.
三. 所有的异常
至此我们无法将一些特殊的异常返回为json格式, 比如客户端提交的数据不符合规范等 在程序中, 我们会对所有的已知异常跑出一个ApiException的异常.但是无法对未知的异常琢一处理, 因此我们可以使用AOP的思想, 在程序的出口统一处理, 直接在入口函数中编写一个函数
# 为了捕捉所有的异常, 我们需要绑定异常的基类 Exception, Flask>1.0
@app.errorhandler(Exception)
def framework_error(e):
# ApiExcetion
# HttpException
# Exception
if isinstance(e, ApiException):
return e
if isinstance(e, HTTPException):
code = e.code
msg = e.description
error_code = 1007
return ApiException(code=code, msg=msg, error_code=error_code)
else:
if not app.config['DEBUG']:
return ApiException()
raise e
此时便可以捕捉所有异常了.
当然在一些正常的返回中也可以使用这个ApiException. 比如:
class Success(ApiException):
code = 200
msg = 'success'
error_code = 0
@api.route('/register', methods=['POST'])
def create_client():
#...
return Success()