flask 全局异常捕获
1.了解falsk异常处理
flask内部异常通过继承这个HTTPException类来处理
class HTTPException(Exception):
"""The base class for all HTTP exceptions. This exception can be called as a WSGI
application to render a default error page or you can catch the subclasses
of it independently and render nicer error messages.
"""
code: t.Optional[int] = None
description: t.Optional[str] = None
def __init__(
self,
description: t.Optional[str] = None,
response: t.Optional["Response"] = None,
) -> None:
super().__init__()
if description is not None:
self.description = description
self.response = response
@classmethod
def wrap(
cls, exception: t.Type[BaseException], name: t.Optional[str] = None
) -> t.Type["HTTPException"]:
"""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.
.. deprecated:: 2.0
Will be removed in Werkzeug 2.1. Create a subclass manually
instead.
.. 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.
"""
warnings.warn(
"'HTTPException.wrap' is deprecated and will be removed in"
" Werkzeug 2.1. Create a subclass manually instead.",
DeprecationWarning,
stacklevel=2,
)
class newcls(cls, exception): # type: ignore
_description = cls.description
show_exception = False
def __init__(
self, arg: t.Optional[t.Any] = None, *args: t.Any, **kwargs: t.Any
) -> None:
super().__init__(*args, **kwargs)
if arg is None:
exception.__init__(self)
else:
exception.__init__(self, arg)
@property
def description(self) -> str:
if self.show_exception:
return (
f"{self._description}\n"
f"{exception.__name__}: {exception.__str__(self)}"
)
return self._description # type: ignore
@description.setter
def description(self, value: str) -> None:
self._description = value
newcls.__module__ = sys._getframe(1).f_globals["__name__"]
name = name or cls.__name__ + exception.__name__
newcls.__name__ = newcls.__qualname__ = name
return newcls
@property
def name(self) -> str:
"""The status name."""
from .http import HTTP_STATUS_CODES
return HTTP_STATUS_CODES.get(self.code, "Unknown Error") # type: ignore
def get_description(
self,
environ: t.Optional["WSGIEnvironment"] = None,
scope: t.Optional[dict] = None,
) -> str:
"""Get the description."""
if self.description is None:
description = ""
elif not isinstance(self.description, str):
description = str(self.description)
else:
description = self.description
description = escape(description).replace("\n", "<br>")
return f"<p>{description}</p>"
def get_body(
self,
environ: t.Optional["WSGIEnvironment"] = None,
scope: t.Optional[dict] = None,
) -> str:
"""Get the HTML body."""
return (
'<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">\n'
f"<title>{self.code} {escape(self.name)}</title>\n"
f"<h1>{escape(self.name)}</h1>\n"
f"{self.get_description(environ)}\n"
)
def get_headers(
self,
environ: t.Optional["WSGIEnvironment"] = None,
scope: t.Optional[dict] = None,
) -> t.List[t.Tuple[str, str]]:
"""Get a list of headers."""
return [("Content-Type", "text/html; charset=utf-8")]
def get_response(
self,
environ: t.Optional[t.Union["WSGIEnvironment", "WSGIRequest"]] = None,
scope: t.Optional[dict] = None,
) -> "Response":
"""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 as WSGIResponse # noqa: F811
if self.response is not None:
return self.response
if environ is not None:
environ = _get_environ(environ)
headers = self.get_headers(environ, scope)
return WSGIResponse(self.get_body(environ, scope), self.code, headers)
截取这个类比较重要的几个方法分析
-
get_headers方法定义了这个返回的响应头,返回的是html文档。
-
get_body方法定义了返回的响应体,对应也是一段html的内容。
最后在Response中将响应体,状态码,响应头定义好返回。
分析至此,其实这个HTTPException中做的事也不难理解,就是定义好响应体,状态码,还有响应头,做了一个返回。当然这个类返回是html类型的,现在前后端分离交互都是json形式的返回,所以我们可以继承自这个类,定义我们自己的异常处理类。
2.自定义异常处理类(json格式)
import typing as t
from werkzeug.exceptions import HTTPException
class APIException(HTTPException):
code = 500
msg = 'Mistake'
error_code = 90001
def __init__(self, code=None, msg=None, data={}, http_code=None, headers=None):
if code:
self.code = code
if http_code:
self.http_code = http_code
if msg:
self.msg = msg
self.data = data
super(APIException, self).__init__(msg, None)
def get_body(
self,
environ: t.Optional["WSGIEnvironment"] = None,
scope: t.Optional[dict] = None,
) -> str:
body = dict(
code=self.code,
msg=self.msg,
data=self.data
# request=request.method + ' ' + self.get_url_no_param()
)
text = json.dumps(body)
return text
def get_headers(
self,
environ: t.Optional["WSGIEnvironment"] = None,
scope: t.Optional[dict] = None,
) -> t.List[t.Tuple[str, str]]:
"""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]
3.自定义错误类
1.定义:
class NotFound(APIException):
http_code = 404
msg = 'the resource are not found'
code = 1001
2.使用:
def test(*arge, **kwargs):
raise NotFound()
3.返回:
4.全局异常捕获
@app.errorhandler(Exception)
def framework_error(e):
logger.exception(e)
if isinstance(e, APIException):
return e
if isinstance(e, HTTPException):
http_code = e.code
msg = e.description
code = 9000
return APIException(code, msg, http_code=code)
else:
return json_response(StatusCode.SEVER_ERROR)
这里对于flask中抛出的所有的错误进行捕获,然后先进行日志的记录。然后判断如果是我们自定义的APIException,就直接返回。如果不是我们自定义的,但是是flask处理的HTTPException,包装成我们自定义的APIException再返回。如果都不是的话,说明是服务器出现的其他错误,问题一般出在我们的代码上,统一返回服务器错误。
5.精简版全局异常捕获
from werkzeug.exceptions import HTTPException
@app.errorhandler(Exception)
def framework_error(e):
logger.exception(e)
if isinstance(e, HTTPException):
return '', e.code
return json_response(StatusCode.SEVER_ERROR, err=str(e))
不对异常进行分类,捕获所有的异常,记录日志,返回服务器错误。(不需要上边的第2,3两步)
自定义错误码:https://www.cnblogs.com/yimeimanong/p/16775602.html