[Django 0-1] Core.Handlers 模块

Core.Handlers 模块

这个模块封装了 wsgi,asgi 两个类,分别用于处理外部的请求信息,asgi 提供异步处理能力。

Handler 模块将请求Request封装包裹了Middleware中间件,并将处理结果返回为Response响应对象。

BaseHandler

重要函数

  • load_middleware: 先把 handler 函数套一层 exception handler, 在倒序包裹中间件。支持process_view,process_exception,process_template_response

    def load_middleware(self, is_async=False):
        """
        Populate middleware lists from settings.MIDDLEWARE.

        Must be called after the environment is fixed (see __call__ in subclasses).
        """
        self._view_middleware = []
        self._template_response_middleware = []
        self._exception_middleware = []

        get_response = self._get_response_async if is_async else self._get_response
        handler = convert_exception_to_response(get_response)
        handler_is_async = is_async
        for middleware_path in reversed(settings.MIDDLEWARE):
            middleware = import_string(middleware_path)
            middleware_can_sync = getattr(middleware, "sync_capable", True)
            middleware_can_async = getattr(middleware, "async_capable", False)
            if not middleware_can_sync and not middleware_can_async:
                raise RuntimeError(
                    "Middleware %s must have at least one of "
                    "sync_capable/async_capable set to True." % middleware_path
                )
            elif not handler_is_async and middleware_can_sync:
                middleware_is_async = False
            else:
                middleware_is_async = middleware_can_async
            try:
                # Adapt handler, if needed.
                # 异步兼容步骤
                adapted_handler = self.adapt_method_mode(
                    middleware_is_async,
                    handler,
                    handler_is_async,
                    debug=settings.DEBUG,
                    name="middleware %s" % middleware_path,
                )
                mw_instance = middleware(adapted_handler)
            except MiddlewareNotUsed as exc:
                if settings.DEBUG:
                    if str(exc):
                        logger.debug("MiddlewareNotUsed(%r): %s", middleware_path, exc)
                    else:
                        logger.debug("MiddlewareNotUsed: %r", middleware_path)
                continue
            else:
                handler = adapted_handler

            if mw_instance is None:
                # 避免你的middleware有问题
                raise ImproperlyConfigured(
                    "Middleware factory %s returned None." % middleware_path
                )

            if hasattr(mw_instance, "process_view"):
                self._view_middleware.insert(
                    0,
                    self.adapt_method_mode(is_async, mw_instance.process_view),
                )
            if hasattr(mw_instance, "process_template_response"):
                self._template_response_middleware.append(
                    self.adapt_method_mode(
                        is_async, mw_instance.process_template_response
                    ),
                )
            if hasattr(mw_instance, "process_exception"):
                # The exception-handling stack is still always synchronous for
                # now, so adapt that way.
                self._exception_middleware.append(
                    self.adapt_method_mode(False, mw_instance.process_exception),
                )

            handler = convert_exception_to_response(mw_instance)
            handler_is_async = middleware_is_async

        # Adapt the top of the stack, if needed.
        handler = self.adapt_method_mode(is_async, handler, handler_is_async)
        # We only assign to this when initialization is complete as it is used
        # as a flag for initialization being complete.
        self._middleware_chain = handler

  • resolve_request: 通过get_resolver得到路由解析器,并将处理结果赋值给request.resolver_match

get_resolver使用了lru_cache装饰器来减少重复计算。


    def resolve_request(self, request):
        """
        Retrieve/set the urlconf for the request. Return the view resolved,
        with its args and kwargs.
        """
        # Work out the resolver.
        if hasattr(request, "urlconf"):
            urlconf = request.urlconf
            set_urlconf(urlconf)
            resolver = get_resolver(urlconf)
        else:
            resolver = get_resolver()
        # Resolve the view, and assign the match object back to the request.
        resolver_match = resolver.resolve(request.path_info)
        request.resolver_match = resolver_match
        return resolver_match

  • _get_response: 调用resolve_request得到处理结果,并将结果赋值给response

看这段源码,你可以知道一个请求是如何被返回的,

  1. 首先得到 urlresolver 解析器,解析请求的 url,得到视图函数和参数。 由于实现了__getitem__可以支持自动解包
  2. 调用process_view函数
  3. 根据配置决定是否开启 atomic
  4. 调用视图函数,并将结果赋值给response
  5. 如果response支持模板渲染,则调用process_template_response函数
  6. 返回response

    def _get_response(self, request):
        """
        Resolve and call the view, then apply view, exception, and
        template_response middleware. This method is everything that happens
        inside the request/response middleware.
        """
        response = None
        callback, callback_args, callback_kwargs = self.resolve_request(request)

        # Apply view middleware
        for middleware_method in self._view_middleware:
            response = middleware_method(
                request, callback, callback_args, callback_kwargs
            )
            if response:
                break

        if response is None:
            wrapped_callback = self.make_view_atomic(callback)
            # If it is an asynchronous view, run it in a subthread.
            if iscoroutinefunction(wrapped_callback):
                wrapped_callback = async_to_sync(wrapped_callback)
            try:
                response = wrapped_callback(request, *callback_args, **callback_kwargs)
            except Exception as e:
                response = self.process_exception_by_middleware(e, request)
                if response is None:
                    raise

        # Complain if the view returned None (a common error).
        self.check_response(response, callback)

        # If the response supports deferred rendering, apply template
        # response middleware and then render the response
        if hasattr(response, "render") and callable(response.render):
            for middleware_method in self._template_response_middleware:
                response = middleware_method(request, response)
                # Complain if the template response middleware returned None
                # (a common error).
                self.check_response(
                    response,
                    middleware_method,
                    name="%s.process_template_response"
                    % (middleware_method.__self__.__class__.__name__,),
                )
            try:
                response = response.render()
            except Exception as e:
                response = self.process_exception_by_middleware(e, request)
                if response is None:
                    raise

        return response

WSGIHandler

重要函数
  • __call__: 处理每个进来的请求

    def __call__(self, environ, start_response):
        set_script_prefix(get_script_name(environ))
        signals.request_started.send(sender=self.__class__, environ=environ)
        request = self.request_class(environ)
        response = self.get_response(request)

        response._handler_class = self.__class__

        status = "%d %s" % (response.status_code, response.reason_phrase)
        response_headers = [
            *response.items(),
            *(("Set-Cookie", c.output(header="")) for c in response.cookies.values()),
        ]
        start_response(status, response_headers)
        if getattr(response, "file_to_stream", None) is not None and environ.get(
            "wsgi.file_wrapper"
        ):
            # If `wsgi.file_wrapper` is used the WSGI server does not call
            # .close on the response, but on the file wrapper. Patch it to use
            # response.close instead which takes care of closing all files.
            response.file_to_stream.close = response.close
            response = environ["wsgi.file_wrapper"](
                response.file_to_stream, response.block_size
            )
        return response

ASGIHandler

重要函数
  • handle: 处理每个进来的请求

    async def handle(self, scope, receive, send):
        """
        Handles the ASGI request. Called via the __call__ method.
        """
        # Receive the HTTP request body as a stream object.
        try:
            body_file = await self.read_body(receive)
        except RequestAborted:
            return
        # Request is complete and can be served.
        set_script_prefix(get_script_prefix(scope))
        await signals.request_started.asend(sender=self.__class__, scope=scope)
        # Get the request and check for basic issues.
        request, error_response = self.create_request(scope, body_file)
        if request is None:
            body_file.close()
            await self.send_response(error_response, send)
            await sync_to_async(error_response.close)()
            return

        async def process_request(request, send):
            response = await self.run_get_response(request)
            try:
                await self.send_response(response, send)
            except asyncio.CancelledError:
                # Client disconnected during send_response (ignore exception).
                pass

            return response

        # Try to catch a disconnect while getting response.
        tasks = [
            # Check the status of these tasks and (optionally) terminate them
            # in this order. The listen_for_disconnect() task goes first
            # because it should not raise unexpected errors that would prevent
            # us from cancelling process_request().
            asyncio.create_task(self.listen_for_disconnect(receive)),
            asyncio.create_task(process_request(request, send)),
        ]
        await asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED)
        # Now wait on both tasks (they may have both finished by now).
        for task in tasks:
            if task.done():
                try:
                    task.result()
                except RequestAborted:
                    # Ignore client disconnects.
                    pass
                except AssertionError:
                    body_file.close()
                    raise
            else:
                # Allow views to handle cancellation.
                task.cancel()
                try:
                    await task
                except asyncio.CancelledError:
                    # Task re-raised the CancelledError as expected.
                    pass

        try:
            response = tasks[1].result()
        except asyncio.CancelledError:
            await signals.request_finished.asend(sender=self.__class__)
        else:
            await sync_to_async(response.close)()

        body_file.close()

可以学习的地方

  • asyncio.wait 的参数return_when=asyncio.FIRST_COMPLETED,可以让任务在第一个完成的时候返回,而不是等待所有任务完成。

总结

Handler 模块封装了 wsgi,asgi 两个类,分别用于处理外部的请求信息,asgi 提供异步处理能力。很好的实现了请求的处理流程,并提供了中间件的功能。

相关推荐

  1. [Django 0-1] Core.Cache模块

    2024-03-16 06:36:04       22 阅读
  2. [Django 0-1] Core.Checks 模块

    2024-03-16 06:36:04       24 阅读
  3. [Django 0-1] Core.Handlers 模块

    2024-03-16 06:36:04       20 阅读
  4. [Django 0-1] Core.Serializers 模块

    2024-03-16 06:36:04       11 阅读
  5. [Django 0-1] Core.Files

    2024-03-16 06:36:04       20 阅读
  6. [Django 0-1] 源码分析

    2024-03-16 06:36:04       20 阅读
  7. Django5.0发布

    2024-03-16 06:36:04       40 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-03-16 06:36:04       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-03-16 06:36:04       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-03-16 06:36:04       19 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-03-16 06:36:04       20 阅读

热门阅读

  1. 如何用树莓派实现视频的warping

    2024-03-16 06:36:04       22 阅读
  2. C语言经典面试题目(十三)

    2024-03-16 06:36:04       17 阅读
  3. 【前端框架的发展史详细介绍】

    2024-03-16 06:36:04       20 阅读
  4. 多线程C++更新MYSQL

    2024-03-16 06:36:04       21 阅读
  5. 前端框架的发展史

    2024-03-16 06:36:04       21 阅读
  6. hive行转列函数stack(int n, v_1, v_2, ..., v_k)

    2024-03-16 06:36:04       19 阅读
  7. WPF实现拖动控件功能(类似从工具箱拖出工具)

    2024-03-16 06:36:04       20 阅读
  8. C++中using 和 typedef 的区别

    2024-03-16 06:36:04       18 阅读
  9. 半监督学习--一起学习吧之人工智能

    2024-03-16 06:36:04       23 阅读