Django应用程序入口WSGIHandler源码解析


Posted in Python onAugust 05, 2019

前言

WSGI 有三个部分, 分别为服务器(server), 应用程序(application) 和中间件(middleware). 已经知道, 服务器方面会调用应用程序来处理请求, 在应用程序中有真正的处理逻辑, 在这里面几乎可以做任何事情, 其中的中间件就会在里面展开.

Django 中的应用程序

任何的 WSGI 应用程序, 都必须是一个 start_response(status, response_headers, exc_info=None) 形式的函数或者定义了 __call__ 的类. 而 django.core.handlers 就用后一种方式实现了应用程序: WSGIHandler. 在这之前, Django 是如何指定自己的 application 的, 在一个具体的 Django 项目中, 它的方式如下:

在 mysite.settings.py 中能找到如下设置:

# Python dotted path to the WSGI application used by Django's runserver.
WSGI_APPLICATION = 'tomato.wsgi.application'

如你所见, WSGI_APPLICATION 就指定了应用程序. 而按图索骥下去, 找到项目中的 wsgi.py, 已经除去了所有的注释:

import os 
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "tomato.settings")
from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()

因此, WSGI_APPLICATION 所指定的即为 wsgi.py 中的全局变量 application. 故伎重演, 继续找下去. 在 django.core 模块中的 wsgi.py 中找到 get_wsgi_application() 函数的实现:

from django.core.handlers.wsgi import WSGIHandler
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.
 
  """
  """
  # 继承, 但只实现了 __call__ 方法, 方便使用
  class WSGIHandler(base.BaseHandler):
  """
  return WSGIHandler()

在 get_wsgi_application() 中实例化了 WSGIHandler, 并无其他操作.

WSGIHandler

紧接着在 django.core.handler 的 base.py 中找到 WSGIHandler 的实现.

# 继承, 但只实现了 __call__ 方法, 方便使用
class WSGIHandler(base.BaseHandler):
  initLock = Lock() 
  # 关于此, 日后展开, 可以将其视为一个代表 http 请求的类
  request_class = WSGIRequest 
  # WSGIHandler 也可以作为函数来调用
  def __call__(self, environ, start_response):
    # Set up middleware if needed. We couldn't do this earlier, because
    # settings weren't available. 
    # 这里的检测: 因为 self._request_middleware 是最后才设定的, 所以如果为空,
    # 很可能是因为 self.load_middleware() 没有调用成功.
    if self._request_middleware is None:
      with self.initLock:
        try:
          # Check that middleware is still uninitialised.
          if self._request_middleware is None:
            因为 load_middleware() 可能没有调用, 调用一次.
            self.load_middleware()
        except:
          # Unload whatever middleware we got
          self._request_middleware = None
          raise 
    set_script_prefix(base.get_script_name(environ))
    signls.request_started.send(sender=self.__class__) # __class__ 代表自己的类 
    try:
      # 实例化 request_class = WSGIRequest, 将在日后文章中展开, 可以将其视为一个代表 http 请求的类
      request = self.request_class(environ)
 
    except UnicodeDecodeError:
      logger.warning('Bad Request (UnicodeDecodeError)',
        exc_info=sys.exc_info(),
        extra={
          'status_code': 400,
        }
      )
      response = http.HttpResponseBadRequest()
    else:
      # 调用 self.get_response(), 将会返回一个相应对象 response<br>      ############# 关键的操作, self.response() 可以获取响应数据.     
      response = self.get_response(request)
 
    # 将 self 挂钩到 response 对象
    response._handler_class = self.__class__ 
    try:
      status_text = STATUS_CODE_TEXT[response.status_code]
    except KeyError:
      status_text = 'UNKNOWN STATUS CODE'
     # 状态码
    status = '%s %s' % (response.status_code, status_text) 
    response_headers = [(str(k), str(v)) for k, v in response.items()] 
    # 对于每个一个 cookie, 都在 header 中设置: Set-cookie xxx=yyy
    for c in response.cookies.values():
      response_headers.append((str('Set-Cookie'), str(c.output(header=''))))
 
    # start_response() 操作已经在上节中介绍了
    start_response(force_str(status), response_headers) 
    # 成功返回相应对象
    return response

WSGIHandler 类只实现了 def __call__(self, environ, start_response), 使它本身能够成为 WSGI 中的应用程序, 并且实现 __call__ 能让类的行为跟函数一样, 详见 python __call__ 方法.

def __call__(self, environ, start_response) 方法中调用了 WSGIHandler.get_response() 方法以获取响应数据对象 response. 从 WSGIHandler 的实现来看, 它并不是最为底层的: WSGIHandler 继承自 base.BaseHandler, 在 django.core.handler 的 base.py 中可以找到: class BaseHandler(object):...

这一节服务器部分已经结束, 接下来的便是中间件和应用程序了, 相关内容会在下节的 BaseHandler 中展开. 我已经在 github 备份了 Django 源码的注释: Decode-Django, 有兴趣的童鞋 fork 吧.

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

Python 相关文章推荐
Python实现改变与矩形橡胶的线条的颜色代码示例
Jan 05 Python
Python实现PS滤镜中马赛克效果示例
Jan 20 Python
python调用百度REST API实现语音识别
Aug 30 Python
python 通过SSHTunnelForwarder隧道连接redis的方法
Feb 19 Python
Python read函数按字节(字符)读取文件的实现
Jul 03 Python
python高斯分布概率密度函数的使用详解
Jul 10 Python
使用python-opencv读取视频,计算视频总帧数及FPS的实现
Dec 10 Python
opencv3/C++ 平面对象识别&amp;透视变换方式
Dec 11 Python
Python实现剪刀石头布小游戏(与电脑对战)
Dec 31 Python
python日期与时间戳的各种转换示例
Feb 12 Python
Python实现清理微信僵尸粉功能示例【基于itchat模块】
May 29 Python
Python Sqlalchemy如何实现select for update
Oct 12 Python
详解如何用TensorFlow训练和识别/分类自定义图片
Aug 05 #Python
详解如何从TensorFlow的mnist数据集导出手写体数字图片
Aug 05 #Python
Python获取时间范围内日期列表和周列表的函数
Aug 05 #Python
Django ORM 查询管理器源码解析
Aug 05 #Python
python实现车牌识别的示例代码
Aug 05 #Python
使用python实现滑动验证码功能
Aug 05 #Python
Django 源码WSGI剖析过程详解
Aug 05 #Python
You might like
给海燕B411配件机起死回生配上件
2021/03/02 无线电
咖啡店都有些什么常规豆子呢?有什么风味在里面
2021/03/04 咖啡文化
必须收藏的23个php实用代码片段
2016/02/02 PHP
smarty循环嵌套用法示例分析
2016/07/19 PHP
PHP sdk文档处理常用代码示例解析
2020/12/09 PHP
javascript的函数
2007/01/31 Javascript
ext for eclipse插件安装方法
2008/04/27 Javascript
jquery 表单取值常用代码
2009/12/22 Javascript
jQuery EasyUI NumberBox(数字框)的用法
2010/07/08 Javascript
ajax的hide隐藏问题解决方法
2012/12/11 Javascript
javascript间隔定时器(延时定时器)学习 间隔调用和延时调用
2014/01/13 Javascript
js控制input框只读实现示例
2014/01/20 Javascript
使用JavaScript获取地址栏参数的方法
2014/12/19 Javascript
js实现延时加载Flash的方法
2015/11/26 Javascript
快速解决js动态改变dom元素属性后页面及时渲染的问题
2016/07/06 Javascript
TypeScript学习之强制类型的转换
2016/12/27 Javascript
使用SVG基本操作API的实例讲解
2017/09/14 Javascript
Vue2.0父子组件传递函数的教程详解
2017/10/16 Javascript
值得收藏的vuejs安装教程
2017/11/21 Javascript
全面介绍vue 全家桶和项目实例
2017/12/27 Javascript
JS实现百度网盘任意文件强制下载功能
2018/08/31 Javascript
vue-cli项目无法用本机IP访问的解决方法
2018/09/20 Javascript
python从入门到精通(DAY 1)
2015/12/20 Python
书单|人生苦短,你还不用python!
2017/12/29 Python
详解Python正则表达式re模块
2019/03/19 Python
元组列表字典(莫烦python基础)
2019/04/03 Python
python 实现图片上传接口开发 并生成可以访问的图片url
2019/12/18 Python
tensorflow入门:TFRecordDataset变长数据的batch读取详解
2020/01/20 Python
使用phonegap检测网络状态的方法
2017/03/30 HTML / CSS
详解window.open被浏览器拦截的解决方案
2019/07/18 HTML / CSS
蹦床仓库:Trampoline Warehouse
2018/12/06 全球购物
员工工作表扬信范文
2014/01/13 职场文书
甲午大海战观后感
2015/06/02 职场文书
《丑小鸭》教学反思
2016/02/19 职场文书
教你在 Java 中实现 Dijkstra 最短路算法的方法
2022/04/08 Java/Android
Docker 镜像介绍以及commit相关操作
2022/04/13 Servers