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.返回:
image

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

参考文档:https://www.cnblogs.com/luyuze95/p/12937704.html

posted @ 2022-10-10 14:38  一枚码农  阅读(419)  评论(0编辑  收藏  举报