django从请求到响应的过程深入讲解
django启动
我们在启动一个django项目的时候,无论你是在命令行执行还是在pycharm直接点击运行,其实都是执行'runserver'的操作,而ruserver是使用django自带的的web server,主要用于开发和调试中,而在正式的环境中,一般会使用nginx+uwsgi模式。
无论是哪种方式,当启动一个项目,都会做2件事:
- 创建一个WSGIServer类的实例,接受用户的请求。
- 当一个用户的http请求到达的时,为用户指定一个WSGIHandler,用于处理用户请求与响应,这个Handler是处理整个request的核心。
WSGI
WSGI:全称是Web Server Gateway Interface,WSGI不是服务器,也不用于与程序交互的API,更不是代码,而只是定义了一个接口,用于描述web server如何与web application通信的规范。
当客户端发送一次请求后,最先处理请求的实际上是 web 服务器就是我们经常说的 nginx、Apache 这类的 web
服务器,然后web服务器再把请求交给web应用程序(如django)处理,这中间的中介就是WSGI,它把 web 服务器和 web 框架
(Django) 连接起来。
简单介绍一下WSGI的一些内容,它规定应用是可调用对象(函数/方法),然后它接受2个固定参数:一个是含有服务器端的环境变量,另一个是可调用对象,这个对象用来初始化响应,给响应加上status code状态码和httpt头部,并且返回一个可调用对象。可以看个简单的例子
# 这段代码来自python核心编程
def simplr_wsgi_app(environ, start_response):
# 固定两个参数,django中也使用同样的变量名
status = '200 OK'
headers = [{'Content-type': 'text/plain'}]
# 初始化响应, 必须在返回前调用
start_response(status, headers)
# 返回可迭代对象
return ['hello world!']
django中,实现同样逻辑的是通过WSGIHandler这个类,下面我们也会重点介绍它!
如果对WSGI与uWSGI有兴趣的,推荐大家看这篇文章,WSGI & uwsgi ,大赞!
中间件基本概念
顾名思义,中间件是位于Web服务器端和Web应用之间的,它可以添加额外的功能。当我们创建一个django项目(通过pycharm),它会自动帮我们设置一些必要的中间件。
MIDDLEWARE_CLASSES = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
中间件要么对来自用户的数据进行预处理,然后发送给应用;要么在应用将响应负载返回给用户之前,对结果数据进行一些最终的调整。通俗一点,在django中,中间能够帮我们准备好request这个对象,然后应用可以直接使用request对象获取到各类数据,也帮我们将response添加头部,状态码等。
数据流
当django接受到一个请求时,会初始化一个WSGIHandler,可以在项目下的wsgi.py文件进行跟踪,你就会发现这一个类。
class WSGIHandler(base.BaseHandler):
def __call__(self, environ, start_response):
pass
这个类遵循WSGI应用的规定,它接受2个参数:一个是含有服务器端的环境变量,另一个是可调用对象,返回一个可迭代对象。
这个handler控制了从请求到响应的整个过程,主要流程:
在网上看到另外一张图,更为完整:
大致几个步骤:
1. 用户通过浏览器请求一个页面
2. 请求到达Request Middlewares,中间件对request做一些预处理或者直接response请求
3. URLConf通过urls.py文件和请求的URL找到相应的View
4. View Middlewares被访问,它同样可以对request做一些处理或者直接返回response
5. 调用View中的函数
6. View中的方法可以选择性的通过Models访问底层的数据
7. 所有的Model-to-DB的交互都是通过manager完成的
8. 如果需要,Views可以使用一个特殊的Context
9. Context被传给Template用来生成页面
a.Template使用Filters和Tags去渲染输出
b.输出被返回到View
c.HTTPResponse被发送到Response Middlewares
d.任何Response Middlewares都可以丰富response或者返回一个完全不同的response
e.Response返回到浏览器,呈现给用户
中间类中的顺序与方法
django 的中间件类至少含有以下四个方法中的一个:
process_request、 process_view、process_exception、process_response
WSGIHandler通过load_middleware将这个些方法分别添加到_request_middleware、_view_middleware、_response_middleware 和 _exception_middleware四个列表中。
并不是每个中间件都有这4个方法,如果不存在某个方法,那么在加载的过程中,这个类就被跳过。
for middleware_path in settings.MIDDLEWARE_CLASSES:
···
if hasattr(mw_instance, 'process_request'):
request_middleware.append(mw_instance.process_request)
if hasattr(mw_instance, 'process_view'):
self._view_middleware.append(mw_instance.process_view)
if hasattr(mw_instance, 'process_template_response'):
self._template_response_middleware.insert(0, mw_instance.process_template_response)
if hasattr(mw_instance, 'process_response'):
self._response_middleware.insert(0, mw_instance.process_response)
if hasattr(mw_instance, 'process_exception'):
self._exception_middleware.insert(0, mw_instance.process_exception)
我们可以从源码看出,process request 和 process response的执行加载顺序正好是相反,在循环中,process_request是被append到列表的末尾,而process_request是被insert到最前面的。
(可能有些情况Comment中间件在Session前面,了解加载的顺序就好了)
process_request
举几个中间件的例子
class CommonMiddleware(object):
# 伪代码
def process_request(self, request): # Check for denied User-Agents
if 'HTTP_USER_AGENT' in request.META:
for user_agent_regex in settings.DISALLOWED_USER_AGENTS:
if user_agent_regex.search(request.META['HTTP_USER_AGENT']):
raise PermissionDenied('Forbidden user agent')
host = request.get_host() if settings.PREPEND_WWW and host and not host.startswith('www.'):
host = 'www.' + host
pass
CommonMiddleware的process_request主要是判断用户代理是否符合要求以及在完善URL,如增加www或者末尾加/。
class SessionMiddleware(object):
def process_request(self, request):
session_key = request.COOKIES.get(settings.SESSION_COOKIE_NAME)
request.session = self.SessionStore(session_key)
SessionMiddleware的process_request是把session_key从cookies中取出来然后放到request.session中。
class AuthenticationMiddleware(MiddlewareMixin):
def process_request(self, request):
assert hasattr(request, 'session'), (
"The Django authentication middleware requires session middleware "
"to be installed. Edit your MIDDLEWARE%s setting to insert "
"'django.contrib.sessions.middleware.SessionMiddleware' before "
"'django.contrib.auth.middleware.AuthenticationMiddleware'."
) % ("_CLASSES" if settings.MIDDLEWARE is None else "")
request.user = SimpleLazyObject(lambda: get_user(request))
在前面提过,中间件的加载是按照一定顺序(正反序),
AuthenticationMiddleware的process_request方法基于session中间件被加载过了,然后通过request的session,将用户取出来放入到request.user 。
process_request 应该返回 None 或者 HTTPResponse 对象。当返回 None 时,WSGI handler
会继续加载 process_request
里面的方法,如果是后一种情况,那么Handlers会直接加载_response_middleware的列表,然后直接response。
解析url
当_request_middleware列表中的 process_request 被遍历完,会得到一个经过处理的request对象(加入了request.session,request.user等属性)。
django将按顺序进行对url进行正则匹配,如果匹配不成功,就会抛出异常。如果request的中间件返回None,那么Django会去解析用户请求的URL。
在setting中有一个ROOT_URLCONF,它指向urls.py文件,根据这个文件可以生产一个urlconf,本质上,他就是url与视图函数之间的映射表,然后通过resolver解析用户的url,找到第一个匹配的view。
process_view
经过url的匹配,会获得视图函数以及相关参数。在调用view函数之前,django会先加载_view_middleware中的各个process_view方法。
逐个默认的中间件看了一遍,只看到csrf有这个方法
# 伪代码
class CsrfViewMiddleware(object): def process_view(self, request, callback, callback_args, callback_kwargs): if getattr(request, 'csrf_processing_done', False):
return None try:
csrf_token = _sanitize_token(
request.COOKIES[settings.CSRF_COOKIE_NAME])
# Use same token next time
request.META['CSRF_COOKIE'] = csrf_token
except KeyError:
csrf_token = None
if getattr(callback, 'csrf_exempt', False):
return None
pass
这个方法的作用是判断cookiers中是否存在csrf的字段,如果不存在,会直接抛出异常,如果存在,返回None。
view中间件和requst中间件一样,必须返回None或一个httpResponse,如果返回一个httpresponse,那么Handlers会直接加载_response_middleware的列表,然后返回HttpResponse,那么Handlers会直接加载_response_middleware的列表,然后直接response
执行view逻辑
view函数需要满足:
- 基于函数(FBV)或者基于类的(CVB)的视图。
- 接受的参数第一个必须为request,并且需要返回一个response对象。
如果视图函数抛出一个异常,Handler 将会循环遍历_exception_middleware 列表,如果有一个异常被抛出,后面的 process_exception 将不会被执行。
process_response
在这个阶段,我们得到了一个 HTTPResponse 对象,这个对象可能是 process_view 返回的,也可能是视图函数返回的。现在我们将循环访问响应中间件。这是中间件调整数据的最后的机会。举个例子:
class XFrameOptionsMiddleware(object): def process_response(self, request, response):
# Don't set it if it's already in the response
if response.get('X-Frame-Options') is not None:
return response # Don't set it if they used @xframe_options_exempt
if getattr(response, 'xframe_options_exempt', False):
return response response['X-Frame-Options'] = self.get_xframe_options_value(request,
response)
return response
XFrameOptionsMiddleware将X-Frame-Options加入到response当中,防止网站被嵌套、被劫持。
class CsrfViewMiddleware(object):
def process_response(self, request, response):
if getattr(response, 'csrf_processing_done', False):
return response if not request.META.get("CSRF_COOKIE_USED", False):
return response # Set the CSRF cookie even if it's already set, so we renew
# the expiry timer.
response.set_cookie(settings.CSRF_COOKIE_NAME,
request.META["CSRF_COOKIE"],
max_age=settings.CSRF_COOKIE_AGE,
domain=settings.CSRF_COOKIE_DOMAIN,
path=settings.CSRF_COOKIE_PATH,
secure=settings.CSRF_COOKIE_SECURE,
httponly=settings.CSRF_COOKIE_HTTPONLY
)
# Content varies with the CSRF cookie, so set the Vary header.
patch_vary_headers(response, ('Cookie',))
response.csrf_processing_done = True
return response
CsrfViewMiddleware在response中设置csrf cookies
最后
当response的中间件加载完,系统在返回之前会调用WSGI服务器端传过来的start_response方法对象,初始化响应,然后进行response响应。
总结
本文重点在于:
- django启动时,启动了一个WSGIserver以及为每个请求的用户生成一个handler。
- 理解WSGI协议,并且WSGIHandler这个类控制整个请求到响应的流程,以及整个流程的基本过程。
- 中间件的概念,以及每一个process_request, process_response, process_view, process_exception方法在哪个步骤发挥着什么样的作用。
- 中间价的执行时有顺序的,request与view是按照顺序去执行的,而response和exception是反序的,这一步实在WSGIHandler在加载到它的各个列表的时候完成的。
好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对脚本之家的支持。
django从请求到响应的过程深入讲解的更多相关文章
- wrk 压测中请求无法响应问题解决过程
================= 遇到问题 =================$ 直连压测 wrk -c10000 -t100 -d100m http://localhost:9981/order/ ...
- django之请求和响应
一:请求 1. 浏览器向服务器传参的几种方式: 方式一:通过正则的形式进行传递,名字为未命名模式 路由的正则形式: from django.conf.urls import url from . im ...
- servlet请求和响应的过程
1.加载 Servlet类被加载到Java虚拟机中,并且实例化.在这个过程中,web容器(例如tomcat)会调用Servlet类的公开无参构造函数,产生一个Servlet类的实例对象.默认情况下Se ...
- 三、Django之请求与响应-Part 1
一.新建项目 进入你指定的项目保存目录,然后运行下面的命令: $ django-admin startproject mysite 这将在目录下生成一个mysite目录,也就是你的这个Django项目 ...
- Django - 请求与响应、表单、中间件、上下文处理器
请求与响应篇 一.HttpRequest对象 服务器接收到http协议的请求后,会根据报文创建HttpRequest对象.视图函数的第一个参数(request)是HttpRequest对象在djang ...
- 一次完整的HTTP请求与响应涉及哪些知识?
Java技术栈 www.javastack.cn 优秀的Java技术公众号 作者:Ruheng 地址:http://www.jianshu.com/p/c1d6a294d3c0 本文以HTTP请求和响 ...
- Django底层剖析之一次请求到响应的整个流程
As we all know,所有的Web应用,其本质上其实就是一个socket服务端,而用户的浏览器就是一个socket客户端. #!/usr/bin/env python #coding:utf- ...
- CoolBlog开发笔记第5课:请求与响应
教程目录 1.1 CoolBlog开发笔记第1课:项目分析 1.2 CoolBlog开发笔记第2课:搭建开发环境 1.3 CoolBlog开发笔记第3课:创建Django应用 1.4 CoolBlog ...
- 02_Django-路由配置-HTTP协议的请求和响应
02_Django-路由配置-HTTP协议的请求和响应 视频:https://www.bilibili.com/video/BV1vK4y1o7jH 博客:https://blog.csdn.net/ ...
随机推荐
- rest_cherrypy
一.简介 A REST API FOR SALT 二.安装,配置 yum -y install salt-api rest_cherrypy: port: # disable_ssl: True ss ...
- nib must contain exactly one top level object which must be a UICollectionReusableView instance
多了一个
- URAL - 1003:Parity (带权并查集&2-sat)
Now and then you play the following game with your friend. Your friend writes down a sequence consis ...
- python去掉字符串'\xa0'
AssertionError: '5\xa0e\xa0*\xa0*\xa0*\xa05' != '5e***5'mystr = '5\xa0e\xa0*\xa0*\xa0*\xa05'mystr = ...
- HDU 3746:Cyclic Nacklace(KMP循环节)
Cyclic Nacklace Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) ...
- 谈ObjC对象的两段构造模式
前言 Objective-c语言在申请对象的时,需要使用两段构造(Two Stage Creation)的模式.一个对象的创建,需要先调用alloc方法或allocWithZone方法,再调用init ...
- CTF-练习平台-Misc之 这么多数据包
十一.这么多数据包 下载文件后解压,用wireshark打开CTF.pcapng,发现有很多包,快速浏览后发现前面都是攻击机(192.168.116.138)在向目标机(192.168.116.159 ...
- Python & 机器学习入门指导
Getting started with Python & Machine Learning(阅者注:这是一篇关于机器学习的指导入门,作者大致描述了用Python来开始机器学习的优劣,以及如果 ...
- C#常用插件和工具
Code generation(代码自动生成) NVelocity CodeSmith X-Code .NET XGoF - NMatrix / DEVerest Compilation(编译工具) ...
- 消息队列在VB.NET数据库开发中的应用
我们先简单的了解一下什么是消息队列(MSMQ)?消息队列是 Windows 2000(NT也有MSMQ,WIN95/98/me/xp不含消息队列服务但是支持客户端的运行)操作系统中通讯的基础,也是用于 ...