背景
在使用flask+flask-restplus時,業務正常時接口函數返回一個可以json序列化的對象
@ns.route('/hello')
class Hello(Resource):
def get(self):
return ['hello', 'hi']
接口返回內容如下:
[
"hello",
"hi"
]
當業務異常(比如檢測到參數錯誤)時,一般調用abort函數,它會拋出一個HTTPException
@ns.route('/error')
class Error(Resource):
def get(self):
abort(400, 'params error')
接口返回內容如下:
{
"message": "params error"
}
由於公司規範要求,需要使用統一的http請求響應模板,如下:
{
'code': 100000,
'message': 'xxx'
'data': 'hello'
}
因此,我們需要對接口返回格式進行改造。
接口返回值統一處理
爲了不改變原有的編碼習慣,又能夠達到以上返回格式的要求,我們可以使用flask-restplus的representation裝飾器來註冊一個接口返回內容處理器
@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
現在再來請求上面的兩個接口,返回格式變成了我們想要的格式
{"code": 100000, "data": ["hello", "hi"]}
{"code": 200000, "message": "params error"}
非HTTPException的異常處理
如果拋出不是HTTPException的異常,會發現並不能得到具體的異常內容,只會得到以下結果:
{"code": 200000, "message": "Internal Server Error"}
如果想要將一些指定的異常內容返回,比如我們自定義了以下的異常類:
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裝飾器來註冊一個異常處理器,在這個處理器中將自定義異常轉換爲正常接口返回格式
@api.errorhandler(BaseException)
def handle_base_exception(error):
return error.to_dict(), error.http_code
這樣自定義異常也可以返回異常內容了
{"code": 200001, "message": "權限不足"}
當然我們也不能將所有異常一股腦拋出,這樣做的話會將因程序不健壯導致的異常也拋出,會給用戶帶來不好的體驗。