• Django(wsgi,middleware,url源码剖析)


    上图是Django基础逻辑图

    ①Django里封装了WSGI模块,用于循环监听socket链接,当客户端发送WEB请求,wsgi就会与客户端建立连接,从而发送数据。

    ②socket通信建立以后,在用户请求进入Django之前,会经历一层中间件的筛选:

    具体流程如下:

    1.process_request

    2.peocess_view

    3.process_exception

    4.process_template_response

    5.process_response

    其中3,4环节我的理解是互斥的,当出现exception,就不会在出现整场渲染的页面,process_template_response也就不会执行,如果出现了渲染页面那么也就意味着异常没有被触发。

    ③URL是Django的路由匹配(其表现形式是Djangox项目中的views.py文件,通过正则匹配按照用户请求的url地址寻找与之对应的view视图函数)。

    注意:由于路由匹配是按照列表遍历的形式自上而下匹配的,所以如果陈旭里路由匹配定义母虎不清,会出现匹配不到或者无法匹配的情况。

    ④当匹配到相应的视图函数,对于动态页面就会从后端的数据库里获取前端页面需要的数据,传递方式为视图函数中return值中附加的字典形式。

    ⑤由于页面渲染为相对比较复杂的一个过程,所以我们将页面的渲染放在Template模板中,具体的应用方式为模板语言的使用,在之后的博客中,会详细说明,今天主要叙述wsgi,middleware,url

    一,wsgi

    新建Django项目,在项目文件夹中,会有wsgi.py文件。

    代码如下;

    """
    WSGI config for 数据库创建 project.

    It exposes the WSGI callable as a module-level variable named ``application``.

    For more information on this file, see
    https://docs.djangoproject.com/en/1.11/howto/deployment/wsgi/
    """

    import os

    from django.core.wsgi import get_wsgi_application

    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "数据库创建.settings")

    application = get_wsgi_application()

    这是Django内部定义的一个函数,ctrl+左键点击进入该函数

    def get_wsgi_application():
    """
    The public interface to Django's WSGI support. Should return a WSGI
    callable.

    Allows us to avoid making django.core.handlers.WSGIHandler public API, in
    case the internal WSGI implementation changes or moves in the future.
    """
    django.setup(set_prefix=False)
    return WSGIHandler()

    我们注意到return值为
    WSGIHandler()

    继续ctrl+左键点击进入

    class WSGIHandler(base.BaseHandler):
    request_class = WSGIRequest

    def __init__(self, *args, **kwargs):
    super(WSGIHandler, self).__init__(*args, **kwargs)
    self.load_middleware()

    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 = [(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

    在看他的父类之前,我们先进入request_class = WSGIRequest
    class WSGIRequest(http.HttpRequest):
      我们可以看出这是另外一个继承过来的类

    不死不休,进入这个类(http.HttpRequest)

    class HttpRequest(object):
    """A basic HTTP request."""

    # The encoding used in GET/POST dicts. None means use default setting.
    _encoding = None
    _upload_handlers = []

    def __init__(self):
    # WARNING: The `WSGIRequest` subclass doesn't call `super`.
    # Any variable assignment made here should also happen in
    # `WSGIRequest.__init__()`.

    self.GET = QueryDict(mutable=True)
    self.POST = QueryDict(mutable=True)
    self.COOKIES = {}
    self.META = {}
    self.FILES = MultiValueDict()

    self.path = ''
    self.path_info = ''
    self.method = None
    self.resolver_match = None
    self._post_parse_error = False
    self.content_type = None
    self.content_params = None
    这时我们已基本可以看到我们请请求头的一些信息,这就是我们给wsgi指定的一些请求头信息,终于,我们找到了比较原始的socket连接部分,开不开心,激不激动?
    WSGIHandler是Django定义的一个类(该类继承base.BaseHandler),老规矩,点击进入父类

    由于父类代码实在太长了,这里我只截取父类的基本信息外加文字注释
    class BaseHandler(object):

    def __init__(self):
    self._request_middleware = None
    self._view_middleware = None
    self._template_response_middleware = None
    self._response_middleware = None
    self._exception_middleware = None
    self._middleware_chain = None
      
      def load_middleware(self):
          该函数主要是读取settings配置文件中的中间件信息,并利用类的反射执行相应的中间件函数。




    其实从这两个类的执行我们基本可以看出来Django程序的执行顺序。

    signal在程序初期,就已开启监听状态,这也就是后来为什么我们写自定义信号会写在__init__文件中,因为信号的监听应该体现在程序的整个生命周期中

    request变量的初始化,这就是我们在发送接收请求时赖以使用的变量,他封装了我们web请求的大部分信息。

    Cookie的使用,在程序初期,我们使用Cookie纪录我们每一次访问的标识位

    一切我们在Django中利用到的神奇变量,全部都在我们神奇的类中。

    好,接下来我们聊聊中间件!

    因为我们的settings配置文件里都是字符串格式的中间件,这时,我们就可以把某一个字符串copy一下,按照他的路径去引入一下,这时我们就可以看到Django最初的中间件是怎么定义的了

    这里我随便找了一个中间件做实验哈。

    from django.middleware.security import SecurityMiddleware
    MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    ]
    看到我是怎么import的了吗?非常简单哦,哈哈

    老规矩,点进去这个东东看看他到底是什么????


    class SecurityMiddleware(MiddlewareMixin):
    def __init__(self, get_response=None):
    self.sts_seconds = settings.SECURE_HSTS_SECONDS
    self.sts_include_subdomains = settings.SECURE_HSTS_INCLUDE_SUBDOMAINS
    self.sts_preload = settings.SECURE_HSTS_PRELOAD
    self.content_type_nosniff = settings.SECURE_CONTENT_TYPE_NOSNIFF
    self.xss_filter = settings.SECURE_BROWSER_XSS_FILTER
    self.redirect = settings.SECURE_SSL_REDIRECT
    self.redirect_host = settings.SECURE_SSL_HOST
    self.redirect_exempt = [re.compile(r) for r in settings.SECURE_REDIRECT_EXEMPT]
    self.get_response = get_response

    def process_request(self, request):
    path = request.path.lstrip("/")
    if (self.redirect and not request.is_secure() and
    not any(pattern.search(path)
    for pattern in self.redirect_exempt)):
    host = self.redirect_host or request.get_host()
    return HttpResponsePermanentRedirect(
    "https://%s%s" % (host, request.get_full_path())
    )

    def process_response(self, request, response):
    if (self.sts_seconds and request.is_secure() and
    'strict-transport-security' not in response):
    sts_header = "max-age=%s" % self.sts_seconds
    if self.sts_include_subdomains:
    sts_header = sts_header + "; includeSubDomains"
    if self.sts_preload:
    sts_header = sts_header + "; preload"
    response["strict-transport-security"] = sts_header

    if self.content_type_nosniff and 'x-content-type-options' not in response:
    response["x-content-type-options"] = "nosniff"

    if self.xss_filter and 'x-xss-protection' not in response:
    response["x-xss-protection"] = "1; mode=block"

    return response

    of course,这又是一个继承过来的类,那么我们再去看看他的父类吧

    class MiddlewareMixin(object):
    def __init__(self, get_response=None):
    self.get_response = get_response
    super(MiddlewareMixin, self).__init__()

    def __call__(self, request):
    response = None
    if hasattr(self, 'process_request'):
    response = self.process_request(request)
    if not response:
    response = self.get_response(request)
    if hasattr(self, 'process_response'):
    response = self.process_response(request, response)
    return response
    这时,我打算用我最浅薄的理解去讲述我们的中间件,在继承MiddlewareMixin父类的基础上,我们自己定义一个类,要注意,我们自定制的中间件想要在哪一个部分做限制

    就在哪一个部分定义我们的类方法:

    1.process_request

    2.peocess_view

    3.process_exception

    4.process_template_response

    5.process_response

    这五个方法你随便定义,如果你只是想要个好看,那么ok,只在函数体内写pass就可以了

    另外,中间件需要在哪里定义呢?答案是想写哪里写哪里?只要你在setting配置文件中引入中间件类的py文件,并且在你的settings中多加一个或者几个字符串就可以了。

    是不是特别方便简单呢?

    哦,接下来是url

    那么,请打开Django中的urls.py文件,我们来分析一下。

    from django.conf.urls import url
    from django.contrib import admin
    from 数据库.views import *

    urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^add/', add),
    ]

    最简单的理解就是把urlpatterns遍历一遍,根据规定好的正则匹配字符串去匹配客户端请求发来的url地址,匹配到就会执行其对应的view视图函数。

    不过为了对自己负责,还是分析一下,虽然这只是作业。

    点金进入url

    def url(regex, view, kwargs=None, name=None):
    if isinstance(view, (list, tuple)):
    # For include(...) processing.
    urlconf_module, app_name, namespace = view
    return RegexURLResolver(regex, urlconf_module, kwargs, app_name=app_name, namespace=namespace)
    elif callable(view):
    return RegexURLPattern(regex, view, kwargs, name)
    else:
    raise TypeError('view must be a callable or a list/tuple in the case of include().')

    我们可以看到固定的位置参数有两个。一个是正则匹配表达式,一个是对应的视图函数。

    要注意,这时我们匹配的view参数是按照列表或者元组的方式去获取的,当匹配成功就会自行分割,那么我们最终的函数体是哪一个呢???

    经过我的思考,他就是namespace,是不是一个非常熟悉的名字,名称空间,我们知道python的每一个变量,每一个韩树明,每一个列名,都是储存在我们的名称空间中,通过关系映射,我们

    可以找到他对应的值,从而执行我们的函数体,得到最终结果。

    也许大家还注意到,我紫色标注的内容,这又是一个类,不行了,我的懒癌又犯了,今天就不写啦,哈哈
















     
  • 相关阅读:
    word-wrap和word-break的区别
    transform 二维转变
    过渡
    新闻下滑导航案例
    background-origin,clip
    边框图片
    背景样式
    线性渐变与径向渐变与重复渐变
    边框阴影
    盒模型和圆角
  • 原文地址:https://www.cnblogs.com/575dsj/p/7623704.html
Copyright © 2020-2023  润新知