美文网首页Starlette 解读 by Gascognya
Starlette 源码阅读 (十一) 异常处理

Starlette 源码阅读 (十一) 异常处理

作者: Gascognya | 来源:发表于2020-08-18 09:33 被阅读0次

    exceptions.py

    class HTTPException(Exception):
        """
        简单的异常类
        """
        def __init__(self, status_code: int, detail: str = None) -> None:
            if detail is None:
                detail = http.HTTPStatus(status_code).phrase
            self.status_code = status_code
            self.detail = detail
    
        def __repr__(self) -> str:
            class_name = self.__class__.__name__
            return f"{class_name}(status_code={self.status_code!r}, detail={self.detail!r})"
    
    ExceptionMiddleware类

    最内层的中间件,用于捕获各种异常

    class ExceptionMiddleware:
        """
        最内层的中间件,其app指向Router
        含有{异常/状态码: 对应处理函数}的字典
        """
        def __init__(
            self, app: ASGIApp, handlers: dict = None, debug: bool = False
        ) -> None:
            self.app = app
            self.debug = debug  # TODO: We ought to handle 404 cases if debug is set.
            self._status_handlers = {}  # type: typing.Dict[int, typing.Callable]
            self._exception_handlers = {
                HTTPException: self.http_exception
            }  # type: typing.Dict[typing.Type[Exception], typing.Callable]
            if handlers is not None:
                for key, value in handlers.items():
                    self.add_exception_handler(key, value)
            # 装载处理映射
    
        def add_exception_handler(
            self,
            exc_class_or_status_code: typing.Union[int, typing.Type[Exception]],
            handler: typing.Callable,
        ) -> None:
            if isinstance(exc_class_or_status_code, int):
                self._status_handlers[exc_class_or_status_code] = handler
            else:
                assert issubclass(exc_class_or_status_code, Exception)
                self._exception_handlers[exc_class_or_status_code] = handler
    
        def _lookup_exception_handler(
            self, exc: Exception
        ) -> typing.Optional[typing.Callable]:
            # 顺着异常的mro链,在自身的异常字典中尝试寻找处理函数
            for cls in type(exc).__mro__:
                if cls in self._exception_handlers:
                    return self._exception_handlers[cls]
            return None
    
        async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
            if scope["type"] != "http":
                await self.app(scope, receive, send)
                return
            # 非http请求,直接过
            response_started = False
            # response触发send的标识
    
            async def sender(message: Message) -> None:
                nonlocal response_started
    
                if message["type"] == "http.response.start":
                    response_started = True
                await send(message)
            # 闭包一个send
            try:
                await self.app(scope, receive, sender)
            except Exception as exc:
                handler = None
                # 捕捉异常
                if isinstance(exc, HTTPException):
                    handler = self._status_handlers.get(exc.status_code)
    
                if handler is None:
                    handler = self._lookup_exception_handler(exc)
    
                if handler is None:
                    raise exc from None
    
                if response_started:
                    msg = "Caught handled exception, but response already started."
                    raise RuntimeError(msg) from exc
                # 捕捉到了异常,但是response已经发送了
    
                request = Request(scope, receive=receive)
                if asyncio.iscoroutinefunction(handler):
                    response = await handler(request, exc)
                else:
                    response = await run_in_threadpool(handler, request, exc)
                await response(scope, receive, sender)
    
        def http_exception(self, request: Request, exc: HTTPException) -> Response:
            if exc.status_code in {204, 304}:
                return Response(b"", status_code=exc.status_code)
            return PlainTextResponse(exc.detail, status_code=exc.status_code)
    

    相关文章

      网友评论

        本文标题:Starlette 源码阅读 (十一) 异常处理

        本文链接:https://www.haomeiwen.com/subject/jkaqjktx.html