Django 响应数据response的返回源码详解


Posted in Python onAugust 06, 2019

响应数据的返回

在 WSGIHandler.__call__(self, environ, start_response) 方法调用了 WSGIHandler.get_response() 方法, 由此得到响应数据对象 response. 如今所要做的, 便是将其返回给客户端. 在 Django 源码小剖: 初探 WSGI 中, 简要的概括了请求到来时 django 自带服务器的执行关系, 摘抄如下:

  • make_server() 中 WSGIServer 类已经作为服务器类, 负责接收请求, 调用 application 的处理, 返回相应;
  • WSGIRequestHandler 作为请求处理类, 并已经配置在 WSGIServer 中;
  • 接着还设置了 WSGIServer.application 属性(set_app(app));
  • 返回 server 实例.
  • 接着打开浏览器, 即发起请求. 服务器实例 WSGIServer httpd 调用自身 handle_request() 函数处理请求. handle_request() 的工作流程如下:请求-->WSGIServer 收到-->调用 WSGIServer.handle_request()-->调用 _handle_request_noblock()-->调用 process_request()-->调用 finish_request()-->finish_request() 中实例化 WSGIRequestHandler-->实例化过程中会调用 handle()-->handle() 中实例化 ServerHandler-->调用 ServerHandler.run()-->run() 调用 application() 这才是真正的逻辑.-->run() 中在调用 ServerHandler.finish_response() 返回数据-->回到 process_request() 中调用 WSGIServer.shutdown_request() 关闭请求(其实什么也没做)

事实上, WSGIServer 并没有负责将响应数据返回给客户端, 它将客户端的信息(如最重要的客户端 socket 套接字)交接给了 WSGIRequestHandler, WSGIRequestHandler 又将客户端的信息交接给了 ServerHandler, 所以 ServerHandler 产生响应数据对象后, 会直接返回给客户端.

代码剖析

从「调用 ServerHandler.run()-->run() 调用 application() 这才是真正的逻辑.-->run() 中在调用 ServerHandler.finish_response() 返回数据」开始说起, 下面是主要的代码解说:

# 下面的函数都在 ServerHandler 的继承链上方法, 有些方法父类只定义了空方法, 具体逻辑交由子类实现. 有关继承链请参看: http://daoluan.net/blog/decode-django-wsgi/
def run(self, application):
 """Invoke the application"""
 try:
  self.setup_environ()
  # application 在 django 中就是 WSGIHandler 类, 他实现了 __call__ 方法, 所以行为和函数一样.
  self.result = application(self.environ, self.start_response)
  self.finish_response()
 except:
  # handle error
 
def finish_response(self):
 try:
  if not self.result_is_file() or not self.sendfile():
   for data in self.result:
    # 向套接字写数据, 将数据返回给客户端
    self.write(data)
   self.finish_content()
 finally:
  self.close()
 
def write(self, data):
 """'write()' callable as specified by PEP 333""" 
 # 必须是都是字符
 assert type(data) is StringType,"write() argument must be string" 
 if not self.status:
  raise AssertionError("write() before start_response()") 
 # 需要先发送 HTTP 头
 elif not self.headers_sent:
  # Before the first output, send the stored headers
  self.bytes_sent = len(data) # make sure we know content-length
  self.send_headers()
 # 再发送实体
 else:
  self.bytes_sent += len(data)
 
 # XXX check Content-Length and truncate if too many bytes written?
 self._write(data)
 self._flush()
 
def write(self, data):
 """'write()' callable as specified by PEP 3333"""
 
 assert isinstance(data, bytes), "write() argument must be bytestring"
 
 # 必须先调用 self.start_response() 设置状态码
 if not self.status:
  raise AssertionError("write() before start_response()")
 
 # 需要先发送 HTTP 头
 elif not self.headers_sent:
  # Before the first output, send the stored headers
  self.bytes_sent = len(data) # make sure we know content-length
  self.send_headers()
 # 再发送实体
 else:
  self.bytes_sent += len(data)
 
 # XXX check Content-Length and truncate if too many bytes written? 是否需要分段发送过大的数据?
 
 # If data is too large, socket will choke, 窒息死掉 so write chunks no larger
 # than 32MB at a time.
 
 # 分片发送
 length = len(data)
 if length > 33554432:
  offset = 0
  while offset < length:
   chunk_size = min(33554432, length)
   self._write(data[offset:offset+chunk_size])
   self._flush()
   offset += chunk_size
 else:
  self._write(data)
  self._flush()
 
def _write(self,data):
 # 如果是第一次调用, 则调用 stdout.write(), 理解为一个套接字对象
 self.stdout.write(data) 
 # 第二次调用就是直接调用 stdout.write() 了
 self._write = self.stdout.write

接下来的事情, 就是回到 WSGIServer 关闭套接字, 清理现场, web 应用程序由此结束; 但服务器依旧在监听(WSGIServer 用 select 实现)是否有新的请求, 不展开了.

阶段性的总结

请求到来至数据相应的流程已经走了一遍, 包括 django 内部服务器是如何运作的, 请求到来是如何工作的, 响应数据对象是如何产生的, url 是如何调度的, views.py 中定义的方法是何时调用的, 响应数据是如何返回的...另外还提出了一个更好的 url 调度策略, 如果你有更好的方法, 不忘与大家分享.

我已经在 github 备份了 Django 源码的注释: Decode-Django, 有兴趣的童鞋 fork 吧.

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
利用Python的Django框架生成PDF文件的教程
Jul 22 Python
python实现将读入的多维list转为一维list的方法
Jun 28 Python
Python零基础入门学习之输入与输出
Apr 03 Python
Django生成PDF文档显示在网页上以及解决PDF中文显示乱码的问题
Jul 04 Python
Python Selenium 之数据驱动测试的实现
Aug 01 Python
Numpy数组array和矩阵matrix转换方法
Aug 05 Python
Python实现RGB与HSI颜色空间的互换方式
Nov 27 Python
python 解决tqdm模块不能单行显示的问题
Feb 19 Python
django列表筛选功能的实现代码
Mar 27 Python
基于selenium及python实现下拉选项定位select
Jul 22 Python
Python 调用C++封装的进一步探索交流
Mar 04 Python
Python数据分析之pandas函数详解
Apr 21 Python
详解Python Matplotlib解决绘图X轴值不按数组排序问题
Aug 05 #Python
Django中提供的6种缓存方式详解
Aug 05 #Python
python修改字典键(key)的方法
Aug 05 #Python
python中使用while循环的实例
Aug 05 #Python
Python3 列表,数组,矩阵的相互转换的方法示例
Aug 05 #Python
Python中print函数简单使用总结
Aug 05 #Python
Numpy数组array和矩阵matrix转换方法
Aug 05 #Python
You might like
PHP实现域名whois查询的代码(数据源万网、新网)
2010/02/22 PHP
PHP合并数组+与array_merge的区别分析
2010/08/01 PHP
php+ajax实现文章自动保存的方法
2014/12/30 PHP
PHP快速排序quicksort实例详解
2016/09/28 PHP
php nginx 实时输出的简单实现方法
2018/01/21 PHP
LaravelS通过Swoole加速Laravel/Lumen详解
2018/03/02 PHP
laravel与thinkphp之间的区别与优缺点
2021/03/02 PHP
JavaScript 基础问答三
2008/12/03 Javascript
JSON 编辑器实现代码
2009/12/06 Javascript
jquery制作居中遮罩层效果分享
2014/02/21 Javascript
让alert不出现弹窗的两种方法
2014/05/18 Javascript
javascript html5移动端轻松实现文件上传
2020/03/27 Javascript
JS集成fckeditor及判断内容是否为空的方法
2016/05/27 Javascript
JS只能输入正整数的简单实例
2016/10/07 Javascript
微信小程序 setData的使用方法详解
2017/04/20 Javascript
JavaScript定义及输出螺旋矩阵的方法详解
2017/12/01 Javascript
高性能的javascript之加载顺序与执行原理篇
2018/01/14 Javascript
vue移动端路由切换实例分析
2018/05/14 Javascript
react.js组件实现拖拽复制和可排序的示例代码
2018/08/20 Javascript
利用es6 new.target来对模拟抽象类的方法
2019/05/10 Javascript
如何使用webpack打包一个库library的方法步骤
2019/12/18 Javascript
JavaScript实现手机号码 3-4-4格式并控制新增和删除时光标的位置
2020/06/02 Javascript
[45:18]2018DOTA2亚洲邀请赛 4.3 突围赛 Optic vs iG 第一场
2018/04/04 DOTA
Python中的字典遍历备忘
2015/01/17 Python
python paramiko模块学习分享
2017/08/23 Python
详解Python3网络爬虫(二):利用urllib.urlopen向有道翻译发送数据获得翻译结果
2019/05/07 Python
对Django项目中的ORM映射与模糊查询的使用详解
2019/07/18 Python
python manage.py runserver流程解析
2019/11/08 Python
使用Python画出小人发射爱心的代码
2019/11/23 Python
Dyson加拿大官方网站:购买戴森吸尘器,风扇,冷热器及配件
2016/10/26 全球购物
美国隐形眼镜网上商店:Lens.com
2019/09/03 全球购物
见习期自我鉴定
2014/01/31 职场文书
致长跑运动员加油稿
2014/02/20 职场文书
《歌唱二小放牛郎》教学反思
2014/04/19 职场文书
欢度春节标语
2014/07/01 职场文书
2015年教师党员公开承诺书
2015/01/22 职场文书