1. 程式人生 > >Django(wsgi,middleware,url源碼剖析)

Django(wsgi,middleware,url源碼剖析)

png cat deploy ref named template 源碼剖析 hand clu

技術分享

上圖是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的每一個變量,每一個韓樹明,每一個列名,都是儲存在我們的名稱空間中,通過關系映射,我們

可以找到他對應的值,從而執行我們的函數體,得到最終結果。

也許大家還註意到,我紫色標註的內容,這又是一個類,不行了,我的懶癌又犯了,今天就不寫啦,哈哈
















 

Django(wsgi,middleware,url源碼剖析)