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

上圖是Django基礎邏輯圖前端

①Django裏封裝了WSGI模塊,用於循環監聽socket連接,當客戶端發送WEB請求,wsgi就會與客戶端創建鏈接,從而發送數據。python

②socket通訊創建之後,在用戶請求進入Django以前,會經歷一層中間件的篩選:web

具體流程以下:數據庫

1.process_requestdjango

2.peocess_view後端

3.process_exceptioncookie

4.process_template_responsesession

5.process_responseapp

其中3,4環節個人理解是互斥的,當出現exception,就不會在出現整場渲染的頁面,process_template_response也就不會執行,若是出現了渲染頁面那麼也就意味着異常沒有被觸發。dom

③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的每個變量,每個韓樹明,每個列名,都是儲存在咱們的名稱空間中,經過關係映射,咱們能夠找到他對應的值,從而執行咱們的函數體,獲得最終結果。也許你們還注意到,我紫色標註的內容,這又是一個類,不行了,個人懶癌又犯了,今天就不寫啦,哈哈
相關文章
相關標籤/搜索