Django 源码阅读(四): WSGI 初探

Python 2017-11-27

起步

在第一章说到,django 使用 WSGIServer 作为内置服务器软件,这个类的实现在 django/core/servers/basehttp.py 文件中定义,这个类继承自 wsgiref.simple_server.WSGIServer 。django中自带的各种 ServerHandler , WSGIServer , WSGIRequestHandler 都基于自python模块 wsgiref 进行及其简单的封装而成的。

Django 的内置服务器

Django 内置服务器在 django.core.serversdjango.core.handlers, 这两者共同来实现。它们基本都是基于 wsgiref.simple_server 继承过来的,关于python本身的标准库中的模块我们就不分析了,这个模块里大致就是从文本级别的解析http包。 从 django.core.servers.basehttp 中:

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

    httpd.set_app(wsgi_handler)
    httpd.serve_forever() # 长期运行

这里的 wsgi_handler 变量是一个 StaticFilesHandler 实例(下文提到)。

WSGI 应用

作为第一个作为wsgi应用,它的定义是在 settings.py 中有个项:

WSGI_APPLICATION = 'webui.wsgi.application'

打开其同目录下的 wsgi.py

import os

from django.core.wsgi import get_wsgi_application

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "webui.settings")
application = get_wsgi_application()

get_wsgi_application() 中实例化了 django.core.handlers.wsgi.WSGIHandler, 并无其他操作。wsgi通过 ServerHandler 来执行django的应用程序,第一个落地对象是 StaticFilesHandler ,从 runserver 的命令执行器 django.contrib.staticfiles.management.commands.runserver 可以得到:

class Command(RunserverCommand):
    ...
    def get_handler(self, *args, **options):
        handler = super(Command, self).get_handler(*args, **options) # handle 即上述中 WSGIHandler 的实例
        use_static_handler = options['use_static_handler']
        insecure_serving = options['insecure_serving']
        if use_static_handler and (settings.DEBUG or insecure_serving):
            return StaticFilesHandler(handler)
        return handler

简单看看 StaticFilesHandler 的构成:

class StaticFilesHandler(WSGIHandler):

    def __init__(self, application):
        self.application = application
        self.base_url = urlparse(self.get_base_url())
        super(StaticFilesHandler, self).__init__()
    def _should_handle(self, path): # 是否是静态文件
        return path.startswith(self.base_url[2]) and not self.base_url[1]

    def __call__(self, environ, start_response):
        if not self._should_handle(get_path_info(environ)):# 如果不是静态文件,交给wsgi应用处理
            return self.application(environ, start_response)

        return super(StaticFilesHandler, self).__call__(environ, start_response)

StaticFilesHandler 的作用是当请求出现时,先检查url是不是一个静态文件请求,如果是的话则进入静态文件(图片、css样式文件、js脚本文件等等)处理的view,如果不是的话则将该请求提交给Django的handler来进行处理(解析url、执行对应的view代码块、渲染和返回template)。

当请求不是静态文件时候,就调用 self.application(environ, start_response) ,也就是 django.core.handlers.wsgi.WSGIHandler 中定义的 __call__ 方法:

class WSGIHandler(base.BaseHandler):
    request_class = WSGIRequest

    def __call__(self, environ, start_response):
        try:
            #  实例化 WSGIRequest
            request = self.request_class(environ)
        except UnicodeDecodeError:
            response = http.HttpResponseBadRequest()
        else:
            # 调用 self.get_response(), 将会返回一个相应对象 response
            response = self.get_response(request)

        response._handler_class = self.__class__ # 将 self 挂钩到 response 对象
        status = '%d %s' % (response.status_code, response.reason_phrase)
        response_headers = [(str(k), str(v)) for k, v in response.items()]
        for c in response.cookies.values():
            response_headers.append((str('Set-Cookie'), str(c.output(header=''))))
        start_response(force_str(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

这个 __call__ 中主要做了两件事,一个是根据字典 environ 实例化了一个 WSGIRequest 对象 request = self.request_class(environ) ,这个也就是开发django中,view视图中作为参数的那个 request。这个对象是根据wsgi接口中的WSGIRequestHandler中的 get_environ() 方法做的工作,针对 environ 进行预处理(例如header、PATH_INFO、REQUEST_METHOD、charset等等。

第二件事是获得 reponse 对象 self.get_response(request) ,有意思的是,在其父类的 get_response 中,通过中间件的机制,执行的可能是 django.contrib.staticfiles.StaticFilesHandler , 也可能是 django.core.handlers.wsgi.WSGIHandler 等。


本文由 hongweipeng 创作,采用 知识共享署名 3.0,可自由转载、引用,但需署名作者且注明文章出处。

如果对您有用,您的支持将鼓励我继续创作!