前言
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()