目錄
- 一、Flask內(nèi)置異常處理
- 二、HTTPException類分析
- 三、自定義異常處理類
- 四、方便的定義自己的錯(cuò)誤類
- 五、注意事項(xiàng)
一、Flask內(nèi)置異常處理
要想在Flask中處理好異常,有一套自己的異常處理機(jī)制,首先,我們必須先知道Flask自己是如何處理異常的。去flask的源碼里找一找會(huì)發(fā)現(xiàn),在flask源碼的app.py文件下,有很多會(huì)拋出異常的方法,其中拿一個(gè)舉例:
def handle_exception(self, e):
"""Default exception handling that kicks in when an exception
occurs that is not caught. In debug mode the exception will
be re-raised immediately, otherwise it is logged and the handler
for a 500 internal server error is used. If no such handler
exists, a default 500 internal server error message is displayed.
.. versionadded:: 0.3
"""
exc_type, exc_value, tb = sys.exc_info()
got_request_exception.send(self, exception=e)
handler = self._find_error_handler(InternalServerError())
if self.propagate_exceptions:
# if we want to repropagate the exception, we can attempt to
# raise it with the whole traceback in case we can do that
# (the function was actually called from the except part)
# otherwise, we just raise the error again
if exc_value is e:
reraise(exc_type, exc_value, tb)
else:
raise e
self.log_exception((exc_type, exc_value, tb))
if handler is None:
return InternalServerError()
return self.finalize_request(handler(e), from_error_handler=True)
我們發(fā)現(xiàn)在flask內(nèi)部對(duì)于500異常,會(huì)拋出這樣一個(gè)錯(cuò)誤類InternalServerError()
class InternalServerError(HTTPException):
......
至此我們發(fā)現(xiàn)flask內(nèi)部異常通過(guò)繼承這個(gè)HTTPException類來(lái)處理,那么這個(gè)HTTPException類就是我們研究的重點(diǎn)。
二、HTTPException類分析
@implements_to_string
class HTTPException(Exception):
"""Baseclass for all HTTP exceptions. This exception can be called as WSGI
application to render a default error page or you can catch the subclasses
of it independently and render nicer error messages.
"""
code = None
description = None
def __init__(self, description=None, response=None):
super(HTTPException, self).__init__()
if description is not None:
self.description = description
self.response = response
@classmethod
def wrap(cls, exception, name=None):
"""Create an exception that is a subclass of the calling HTTP
exception and the ``exception`` argument.
The first argument to the class will be passed to the
wrapped ``exception``, the rest to the HTTP exception. If
``e.args`` is not empty and ``e.show_exception`` is ``True``,
the wrapped exception message is added to the HTTP error
description.
.. versionchanged:: 0.15.5
The ``show_exception`` attribute controls whether the
description includes the wrapped exception message.
.. versionchanged:: 0.15.0
The description includes the wrapped exception message.
"""
class newcls(cls, exception):
_description = cls.description
show_exception = False
def __init__(self, arg=None, *args, **kwargs):
super(cls, self).__init__(*args, **kwargs)
if arg is None:
exception.__init__(self)
else:
exception.__init__(self, arg)
@property
def description(self):
if self.show_exception:
return "{}\n{}: {}".format(
self._description, exception.__name__, exception.__str__(self)
)
return self._description
@description.setter
def description(self, value):
self._description = value
newcls.__module__ = sys._getframe(1).f_globals.get("__name__")
name = name or cls.__name__ + exception.__name__
newcls.__name__ = newcls.__qualname__ = name
return newcls
@property
def name(self):
"""The status name."""
from .http import HTTP_STATUS_CODES
return HTTP_STATUS_CODES.get(self.code, "Unknown Error")
def get_description(self, environ=None):
"""Get the description."""
return u"p>%s/p>" % escape(self.description).replace("\n", "br>")
def get_body(self, environ=None):
"""Get the HTML body."""
return text_type(
(
u'!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">\n'
u"title>%(code)s %(name)s/title>\n"
u"h1>%(name)s/h1>\n"
u"%(description)s\n"
)
% {
"code": self.code,
"name": escape(self.name),
"description": self.get_description(environ),
}
)
def get_headers(self, environ=None):
"""Get a list of headers."""
return [("Content-Type", "text/html; charset=utf-8")]
def get_response(self, environ=None):
"""Get a response object. If one was passed to the exception
it's returned directly.
:param environ: the optional environ for the request. This
can be used to modify the response depending
on how the request looked like.
:return: a :class:`Response` object or a subclass thereof.
"""
from .wrappers.response import Response
if self.response is not None:
return self.response
if environ is not None:
environ = _get_environ(environ)
headers = self.get_headers(environ)
return Response(self.get_body(environ), self.code, headers)
- 截取這個(gè)類比較重要的幾個(gè)方法分析,
get_headers
方法定義了這個(gè)返回的響應(yīng)頭,返回的是html文檔。
get_body
方法定義了返回的響應(yīng)體,對(duì)應(yīng)也是一段html的內(nèi)容。
- 最后在Response中將響應(yīng)體,狀態(tài)碼,響應(yīng)頭定義好返回。
分析至此,其實(shí)這個(gè)HTTPException中做的事也不難理解,就是定義好響應(yīng)體,狀態(tài)碼,還有響應(yīng)頭,做了一個(gè)返回。當(dāng)然這個(gè)類返回是html類型的,現(xiàn)在前后端分離交互都是json形式的返回,所以我們可以繼承自這個(gè)類,定義我們自己的異常處理類。
三、自定義異常處理類
首先我們理解我們自己的這個(gè)異常處理類,應(yīng)該繼承自HTTPException來(lái)改寫(xiě)。而我們自定義的內(nèi)容應(yīng)該包含以下幾點(diǎn):
- 需要定義我們自己想要返回的錯(cuò)誤信息的json格式,比如內(nèi)部錯(cuò)誤碼、錯(cuò)誤信息等我們想記錄的信息。
- 需要更改返回的響應(yīng)頭,返回json格式的信息響應(yīng)頭就應(yīng)該設(shè)為
'Content-Type': 'application/json'
- 同樣需要和HTTPException一樣定義好狀態(tài)碼
如下定義我們自己的異常類APIException,返回的信息包括內(nèi)部錯(cuò)誤碼,錯(cuò)誤信息,請(qǐng)求的url
class APIException(HTTPException):
code = 500
msg = 'sorry, we made a mistake!'
error_code = 999
def __init__(self, msg=None, code=None, error_code=None, headers=None):
if code:
self.code = code
if error_code:
self.error_code = error_code
if msg:
self.msg = msg
super(APIException, self).__init__(msg, None)
def get_body(self, environ=None):
body = dict(
msg=self.msg,
error_code=self.error_code,
request=request.method + ' ' + self.get_url_no_param()
)
text = json.dumps(body)
return text
def get_headers(self, environ=None):
"""Get a list of headers."""
return [('Content-Type', 'application/json')]
@staticmethod
def get_url_no_param():
full_path = str(request.full_path)
main_path = full_path.split('?')
return main_path[0]
四、方便的定義自己的錯(cuò)誤類
有了上面我們改寫(xiě)好的APIException類,我們就可以自由的定義各種狀態(tài)碼的錯(cuò)誤以及對(duì)應(yīng)的錯(cuò)誤信息,然后在合適的位置拋出。比如:
class Success(APIException):
code = 201
msg = 'ok'
error_code = 0
class DeleteSuccess(APIException):
code = 202
msg = 'delete ok'
error_code = 1
class UpdateSuccess(APIException):
code = 200
msg = 'update ok'
error_code = 2
class ServerError(APIException):
code = 500
msg = 'sorry, we made a mistake!'
error_code = 999
class ParameterException(APIException):
code = 400
msg = 'invalid parameter'
error_code = 1000
class NotFound(APIException):
code = 404
msg = 'the resource are not found'
error_code = 1001
class AuthFailed(APIException):
code = 401
msg = 'authorization failed'
error_code = 1005
class Forbidden(APIException):
code = 403
error_code = 1004
msg = 'forbidden, not in scope'
有了這些自定義的錯(cuò)誤類,我們不僅可以直接在需要的地方拋出,而且有了自定義的錯(cuò)誤碼,發(fā)生錯(cuò)誤時(shí),只要對(duì)照錯(cuò)誤碼去查找對(duì)應(yīng)的錯(cuò)誤類,非常方便。而且特別說(shuō)明的是,雖然說(shuō)是錯(cuò)誤類,但是也是可以定義響應(yīng)成功的返回的,比如上面定義的200,201的類,同樣可以作為一個(gè)成功的返回。
使用演示:
user = User.query.first()
if not user:
raise NotFound()
五、注意事項(xiàng)
盡管我們可以在我們認(rèn)為可能出錯(cuò)的所有地方,繼承自己的異常類,定義自己的錯(cuò)誤類,然后拋出,但是也不是所有的異常都是我們可以提前預(yù)知的。比如我們接受前端傳來(lái)的參數(shù),參數(shù)類型或取值范圍不正確,這些我們可以預(yù)知并處理好,但是如果是邏輯處理中出現(xiàn)了問(wèn)題,這些不是我們程序員可以控制并處理。所以光有自定義錯(cuò)誤類還不夠,我們還需要在全局捕獲異常來(lái)判斷,利用AOP思想。
# 全局錯(cuò)誤AOP處理
@app.errorhandler(Exception)
def framework_error(e):
api_logger.error("error info: %s" % e) # 對(duì)錯(cuò)誤進(jìn)行日志記錄
if isinstance(e, APIException):
return e
if isinstance(e, HTTPException):
code = e.code
msg = e.description
error_code = 1007
return APIException(msg, code, error_code)
else:
if not app.config['DEBUG']:
return ServerError()
else:
return e
這里對(duì)于flask中拋出的所有的錯(cuò)誤進(jìn)行捕獲,然后先進(jìn)行日志的記錄。然后判斷如果是我們自定義的APIException,就直接返回。如果不是我們自定義的,但是是flask處理的HTTPException,包裝成我們自定義的APIException再返回。如果都不是的話,說(shuō)明是服務(wù)器出現(xiàn)的其他錯(cuò)誤,問(wèn)題一般出在我們的代碼上,在生產(chǎn)環(huán)境下,一般統(tǒng)一返回一個(gè)500錯(cuò)誤,在調(diào)試模式下,可以原樣返回,便于我們定位修改自己的代碼。
以上就是詳解Flask開(kāi)發(fā)技巧之異常處理的詳細(xì)內(nèi)容,更多關(guān)于Flask異常處理的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
您可能感興趣的文章:- Flask框架學(xué)習(xí)筆記之消息提示與異常處理操作詳解
- flask中主動(dòng)拋出異常及統(tǒng)一異常處理代碼示例
- 利用python實(shí)現(xiàn)后端寫(xiě)網(wǎng)頁(yè)(flask框架)
- 如何創(chuàng)建一個(gè)Flask項(xiàng)目并進(jìn)行簡(jiǎn)單配置
- flask開(kāi)啟多線程的具體方法
- 如何解決flask修改靜態(tài)資源后緩存文件不能及時(shí)更改問(wèn)題
- 詳解Flask前后端分離項(xiàng)目案例
- 基于python和flask實(shí)現(xiàn)http接口過(guò)程解析
- Python Flask微信小程序登錄流程及登錄api實(shí)現(xiàn)代碼
- Python flask框架實(shí)現(xiàn)查詢數(shù)據(jù)庫(kù)并顯示數(shù)據(jù)