背景
在使用flask+flask-restplus时,业务正常时接口函数返回一个可以json序列化的对象
1 2 3 4
| @ns.route('/hello') class Hello(Resource): def get(self): return ['hello', 'hi']
|
接口返回内容如下:
当业务异常(比如检测到参数错误)时,一般调用abort函数,它会抛出一个HTTPException
1 2 3 4
| @ns.route('/error') class Error(Resource): def get(self): abort(400, 'params error')
|
接口返回内容如下:
1 2 3
| { "message": "params error" }
|
由于公司规范要求,需要使用统一的http请求响应模板,如下:
1 2 3 4 5
| { 'code': 100000, 'message': 'xxx' 'data': 'hello' }
|
因此,我们需要对接口返回格式进行改造。
接口返回值统一处理
为了不改变原有的编码习惯,又能够达到以上返回格式的要求,我们可以使用flask-restplus的representation装饰器来注册一个接口返回内容处理器
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @api.representation('application/json') def output_json(data, code, headers=None): result = {} if api.specs_url == request.url: result = data elif code in (200, 201, 204): result['code'] = 100000 result['data'] = data else: result['code'] = data.get('code') or 200000 result['message'] = data['message'] response = make_response(json.dumps(result), code) response.headers.extend(headers or {}) return response
|
现在再来请求上面的两个接口,返回格式变成了我们想要的格式
1 2
| {"code": 100000, "data": ["hello", "hi"]} {"code": 200000, "message": "params error"}
|
非HTTPException的异常处理
如果抛出不是HTTPException的异常,会发现并不能得到具体的异常内容,只会得到以下结果:
1
| {"code": 200000, "message": "Internal Server Error"}
|
如果想要将一些指定的异常内容返回,比如我们自定义了以下的异常类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| class BaseException(Exception): http_code = 500 business_code = 200000
def __init__(self, message=None): if message: self.message = message
def to_dict(self): data = {'code': self.business_code, 'message': self.message} return data
class InsufficientPrivilege(BaseException): http_code = 403 business_code = 200001 message = '权限不足'
|
那么我们可以使用flask-restplus的errorhandler装饰器来注册一个异常处理器,在这个处理器中将自定义异常转换为正常接口返回格式
1 2 3
| @api.errorhandler(BaseException) def handle_base_exception(error): return error.to_dict(), error.http_code
|
这样自定义异常也可以返回异常内容了
1
| {"code": 200001, "message": "权限不足"}
|
当然我们也不能将所有异常一股脑抛出,这样做的话会将因程序不健壮导致的异常也抛出,会给用户带来不好的体验。