3. Django源码阅读 -- 请求处理

大家可以使用pycharm,根据这偏文章来看

django版本: 2.2

上节回顾

上节说到在启动 WSGIServer 的时候,需要获取一个handler,如何加载中间件以及将中间件的对象保存到一个叫_middleware_chain的变量中, 最后启动 WSGIServer 后进入httpd.server_forever()的死循环。

本节内容

首先我们看一下 WSGIServer 类的继承关系:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bAwp1nmV-1598856705330)(./1598599363601.png)]


了解了 WSGIServer 的继承关系后,我们来看代码:在 httpd.server_forever() 死循环中,当有请求过来的时候,就会调用 self._handler_request_noblock(), 此时这里的 selfWSGIServer 对象。 WSGIServer 通过 selector 接收到请求之后,会调用 self._handle_request_noblock() 方法,

def serve_forever(self, poll_interval=0.5):
    self.__is_shut_down.clear()
    try:
        with _ServerSelector() as selector:
            selector.register(self, selectors.EVENT_READ)
            while not self.__shutdown_request:
                ready = selector.select(poll_interval)
                if self.__shutdown_request:
                    break
                if ready:
                    self._handle_request_noblock()
                self.service_actions()
    finally:
        ...
1. _handle_request_noblock

因为这个 _handler_request_noblock() 方法,只在 socketserver.BaseServer 中定义了,那么就直接使用 BaseServer._handler_request_noblock() 方法了,这方法定义如下:

def _handle_request_noblock(self):
    try:
        request, client_address = self.get_request()
    except OSError:
        return
    if self.verify_request(request, client_address):
        try:
            self.process_request(request, client_address)
        except Exception:
            self.handle_error(request, client_address)
            self.shutdown_request(request)
        except:
            self.shutdown_request(request)
            raise
    else:
        self.shutdown_request(request)
2. self.get_request && self.socket.accept

get_request 方法是在 TCPServer 类中实现的,这个方法中只有一条指令: return self.socket.accept(),我们直接看self.socket.accept() 方法。在 TCPServer 类实例化的时候, 实例化一个 socket.socket 的对象,accept 方法,也就是这个对象的方法,这个方法的主要作用是接受这个请求。

注意:这里的 request 只是一个 socket 对象, 并不是我们写view方法中的request,不要误认为 request 对象是在这里生成的

3. self.process_request

重点来了, 这个方法是整条流中的核心,他的作用是接收数据流,生成request对象,对请求做处理,返回等,这个方法主要是启动了一个线程来执行 process_request_thread 方法,然后 process_request_thread 方法又调用了 finish_request 方法,所以我们直接来看 finish_request 方法, finish_request 是在 BaseServer 中实现的,方法中只有一条指令,那么这个 RequestHandlerClass 又是谁呢?

def finish_request(self, request, client_address):
     self.RequestHandlerClass(request, client_address, self)

我们看一下 BaseServer 在实例化的时候,会接受一个 RequestHandlerClass 参数,那我们什么时候传给他的这个参数呢? 那就是我们在启动 WSGIServer 的时候传给他的。还记得启动 WSGIServer 的时候创建的那个 httpd 对象吗? 我们再来看一下 WSGIServer 启动时的代码:

def run(addr, port, wsgi_handler, ipv6=False, threading=False, server_cls=WSGIServer):
    server_address = (addr, port)
    if threading:
        httpd_cls = type('WSGIServer', (socketserver.ThreadingMixIn, server_cls), {})
    else:
        httpd_cls = server_cls
    httpd = httpd_cls(server_address, WSGIRequestHandler, ipv6=ipv6)

在创建 httpd_cls 类的指令是这样的 httpd_cls = type(‘WSGIServer’, (socketserver.ThreadingMixIn, server_cls), {}) ,这个 httpd_cls 继承了 socketserver.ThreadingMixIn, server_cls 两个类, 所以我们实例化 httpd_cls 类的时候,会调用父类的 __init__ 方法,所以这时候这个 RequestHandlerClass 就等于 WSGIRequestHandler,到了这里我们再去看 finish_request 方法,就很容易理解了, finish_request 方法中就是实例化一个 WSGIRequestHandler 的对象。 那我们在看一下实例化这个对象的时候,会做什么? 那我们就看一下 WSGIRequestHandler 这个类的实现。

4. WSGIRequestHandler类的实现

首先我们看一下 WSGIRequestHandler 的继承关系:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YYWgLSog-1598856705341)(./1598599503538.png)]

WSGIServer 同理,进行实例化的时候,会调用 socketserver.BaseRequestHandler__init__ 方法, 那我们就直接看socketserver.BaseRequestHandler__init__ 方法的源码:

def __init__(self, request, client_address, server):
    self.request = request
    self.client_address = client_address
    self.server = server
    self.setup()
    try:
        self.handle()
    finally:
        self.finish()

前面的赋值操作,我们忽略前面的赋值操作,直接看 self.setup() 方法, 首先我们要知道这里的 selfWSGIRequestHandler 类,因为我们正在 finish_request 中实例化 WSGIRequestHandler类。所以这里的 self.setup方法,我们要从 WSGIRequestHandler 类中向上找,所以这里会执行 StreamRequestHandler.setup 方法

 def setup(self):
     self.connection = self.request
     if self.timeout is not None:
         self.connection.settimeout(self.timeout)
     if self.disable_nagle_algorithm:
         self.connection.setsockopt(socket.IPPROTO_TCP,
                                    socket.TCP_NODELAY, True)
     self.rfile = self.connection.makefile('rb', self.rbufsize)
     if self.wbufsize == 0:
         self.wfile = _SocketWriter(self.connection)
     else:
         self.wfile = self.connection.makefile('wb', self.wbufsize)

这里的 setup 方法就是创建与 socket 关联的文件对象,执行完setup后,在去执行handle 方法,在 handle 方法中会调用 handle_one_request 方法,这个方法首先会从 socket 中读取数据,然后调用 parse_request 方法对数据做解析,parse_request 是对 HTTP 版本header长度 做校验。 然后创建一个 ServerHandler 的对象,这个对象在实例化的时候,会读取socket中的数据保存到对象的属性中。当我们在实例化 ServerHandler 的时候,调用了 self.get_environ 方法,这个方法是对请求头部的一个整理到一个 env 的变量中,具体的内容可以去看一下 get_environ 方法的代码,这里不做解释。 最后调用 handler.run 方法,调用 handler.run 的时候,调用了 server.get_app 方法,这个server又是哪里来的呢,还是要看上面 finish_request 方法在实例化 WSGIRequestHandler 的时候,传入了一个 self 参数,这个 **self ** 是 WSGIServer, 这里获取到的 application 是一个 StaticFilesHandler对象, 至于为什么请看上一篇文章:启动WSGIServer

def handle_one_request(self):
    self.raw_requestline = self.rfile.readline(65537)
    if len(self.raw_requestline) > 65536:
        self.requestline = ''
        self.request_version = ''
        self.command = ''
        self.send_error(414)
        return

    if not self.parse_request():  # An error code has been sent, just exit
        return

    handler = ServerHandler(
        self.rfile, self.wfile, self.get_stderr(), self.get_environ()
    )
    handler.request_handler = self      # backpointer for logging & connection closing
    handler.run(self.server.get_app())

我看继续看一下 handler.run 方法,这个方法中,调用了 application,因为 application 是一个 StaticFilesHandler 对象,直接调用的话,那就会调用 StaticFilesHandler 类的 __call__ 方法。

def run(self, application):
     try:
         self.setup_environ()
         self.result = application(self.environ, self.start_response)
         self.finish_response()
     except:
         try:
             self.handle_error()
         except:
             # If we get an error handling an error, just give up already!
             self.close()
             raise   # ...and let the actual server figure it out.

StaticFilesHandler 中,还会调用 StaticFilesHandler 自己的 application属性,具体代码如下,这里的 self 就是 StaticFilesHandler对象自身了, 由上一篇文章可知,这里的 StaticFilesHandlerapplication 是一个 WSGIHandler 对象, 所以这里调用的时候,会直接调用 WSGIHandler__call__ 方法。

if not self._should_handle(get_path_info(environ)):
    return self.application(environ, start_response)
return super().__call__(environ, start_response)

那我们直接看 WSGIHandler__call__ 方法,首先 request = self.request_class(environ) 这条指令就是生成 request 对象的指令,我们在 view 中写的代码,用的就是这个 request对象,这个对象实例化的是什么内容,可以自己去看一下,比较简单。在看一下 response = self.get_response(request) 指令,这条指令调用了 get_response 的方法

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'):
         response = environ['wsgi.file_wrapper'](response.file_to_stream)
     return response

5. get_response方法
那我们在看一下 get_response 方法的内容,这里主要是调用了 _middleware_chain

def get_response(self, request):
    """Return an HttpResponse object for the given HttpRequest."""
    # Setup default url resolver for this thread
    set_urlconf(settings.ROOT_URLCONF)
    response = self._middleware_chain(request)
    response._closable_objects.append(request)
    if response.status_code >= 400:
        log_response(
            '%s: %s', response.reason_phrase, request.path,
            response=response,
            request=request,
        )
    return response

上一篇文章中 我们详细解释了这个 _middleware 的结构,不清楚的同学可以去看一下。

在调用 _middleware 这个方法的时候,首先会调用最外层对象的 __call__ 方法,我们可以看一下中间件的写法,都会继承父类 MiddlewareMixin,当然也会继承 MiddlewareMixin.__call__ 方法,下面是 MiddlewareMixin.__call__ 方法的代码,首先会判断这个对象中有没有 process_request 方法,如果有那就调用,并将返回值传给 response 变量,然后调用自己的 get_response 方法,此时,如果不是在最内曾,那么自己的 get_response 指向的就是内层的中间件对象,然后在执行内层的 process_request 方法,如果是最内层,那么自己的 get_response 方法就是 _get_response 方法了。

def __call__(self, request):
    response = None
    if hasattr(self, 'process_request'):
        response = self.process_request(request)
    response = response or self.get_response(request)
    if hasattr(self, 'process_response'):
        response = self.process_response(request, response)
    return response

那我们看一下 _get_response 方法,这个方法比较长, 我们就慢慢来看,首先会通过 get_resolver 方法创建一个 URLResolver 的对象,用来通过 url 找到你写的 view 方法或者类callback 这个变量就是获取到的方法或者类。然后执行 _view_middleware 列表中的方法。这个列表是在 load_middleware 中处理的,里面存放的是中间件的 process_view 方法,这也就是将每个中间件的 process_view 方法执行一遍。 然后执行自己写的 view 中的类或者方法,然后判断 response 是否为空,是否有 render,之后将 response 返回。

def _get_response(self, request):

    response = None

    if hasattr(request, 'urlconf'):
        urlconf = request.urlconf
        set_urlconf(urlconf)
        resolver = get_resolver(urlconf)
    else:
        resolver = get_resolver()

    resolver_match = resolver.resolve(request.path_info)
    callback, callback_args, callback_kwargs = resolver_match
    request.resolver_match = resolver_match

    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)
        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:
        if isinstance(callback, types.FunctionType):    # FBV
            view_name = callback.__name__
        else:                                           # CBV
            view_name = callback.__class__.__name__ + '.__call__'

        raise ValueError(
            "The view %s.%s didn't return an HttpResponse object. It "
            "returned None instead." % (callback.__module__, view_name)
        )

    elif hasattr(response, 'render') and callable(response.render):
        for middleware_method in self._template_response_middleware:
            response = middleware_method(request, response)
            if response is None:
                raise ValueError(
                    "%s.process_template_response didn't return an "
                    "HttpResponse object. It returned None instead."
                    % (middleware_method.__self__.__class__.__name__)
                )

        try:
            response = response.render()
        except Exception as e:
            response = self.process_exception_by_middleware(e, request)

    return response

在这里执行完 _get_response 之后,我们需要再回到中间件,因为我们是从中间件 __call__ 方法跳过来的, 下一步我们需要判断中间件有没有 process_response 了,如果有的话,我们需要执行一下。

def __call__(self, request):
    response = None
    if hasattr(self, 'process_request'):
        response = self.process_request(request)
    response = response or self.get_response(request)
    if hasattr(self, 'process_response'):
        response = self.process_response(request, response)
    return response

之后,我们处理一下response,关闭连接,这样一次请求就完成了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值