码迷,mamicode.com
首页 > Web开发 > 详细

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

时间:2017-10-03 14:56:37      阅读:293      评论:0      收藏:0      [点我收藏+]

标签:ble   经历   template   move   mat   false   env   prot   view   

技术分享

上图是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源码剖析)

标签:ble   经历   template   move   mat   false   env   prot   view   

原文地址:http://www.cnblogs.com/575dsj/p/7623704.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!