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 相关文章推荐
解决Scrapy安装错误:Microsoft Visual C++ 14.0 is required...
Oct 01 Python
Python简单计算给定某一年的某一天是星期几示例
Jun 27 Python
python命令行工具Click快速掌握
Jul 04 Python
Python selenium的基本使用方法分析
Dec 21 Python
pycharm 2019 最新激活方式(pycharm破解、激活)
Sep 22 Python
python基于event实现线程间通信控制
Jan 13 Python
Python matplotlib画图时图例说明(legend)放到图像外侧详解
May 16 Python
keras多显卡训练方式
Jun 10 Python
在Keras中利用np.random.shuffle()打乱数据集实例
Jun 15 Python
基于matplotlib中ion()和ioff()的使用详解
Jun 16 Python
Python selenium模块实现定位过程解析
Jul 09 Python
Django项目创建及管理实现流程详解
Oct 13 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
利用php+mysql来做一个功能强大的在线计算器
2010/10/12 PHP
最准确的php截取字符串长度函数
2015/10/29 PHP
PHP删除数组中指定值的元素常用方法实例分析【4种方法】
2018/08/21 PHP
input 高级限制级用法
2009/03/26 Javascript
JavaScript中变量提升 Hoisting
2012/07/03 Javascript
JS关闭窗口与JS关闭页面的几种方法小结
2013/12/17 Javascript
jQuery $.extend()用法总结
2014/06/15 Javascript
轻松学习Javascript闭包函数
2015/12/15 Javascript
使用vue.js2.0 + ElementUI开发后台管理系统详细教程(二)
2017/01/21 Javascript
Node.js与Sails redis组件的使用教程
2017/02/14 Javascript
将Sublime Text 3 添加到右键中的简单方法
2017/12/12 Javascript
Vue resource三种请求格式和万能测试地址
2018/09/26 Javascript
微信小程序实现文字从右向左无限滚动
2020/11/18 Javascript
浅谈React Native 传参的几种方式(小结)
2019/05/21 Javascript
js 实现 list转换成tree的方法示例(数组到树)
2019/08/18 Javascript
Javascript和jquery在selenium的使用过程
2019/10/31 jQuery
JavaScript Tab菜单实现过程解析
2020/05/13 Javascript
npm全局环境变量配置详解
2020/12/15 Javascript
Vue2.x-使用防抖以及节流的示例
2021/03/02 Vue.js
Python3之读取连接过的网络并定位的方法
2018/04/22 Python
Python图像处理之简单画板实现方法示例
2018/08/30 Python
python+opencv打开摄像头,保存视频、拍照功能的实现方法
2019/01/08 Python
详解opencv Python特征检测及K-最近邻匹配
2019/01/21 Python
将Python字符串生成PDF的实例代码详解
2019/05/17 Python
Python+opencv 实现图片文字的分割的方法示例
2019/07/04 Python
详解python3中用HTMLTestRunner.py报ImportError: No module named 'StringIO'如何解决
2019/08/27 Python
Python + Flask 实现简单的验证码系统
2019/10/01 Python
Python-jenkins模块之folder相关操作介绍
2020/05/12 Python
兰蔻加拿大官方网站:Lancome加拿大
2016/08/05 全球购物
Oroton中国官网:澳洲知名奢侈配饰品牌
2017/03/26 全球购物
中学图书馆工作总结
2015/08/11 职场文书
小学生禁毒教育心得体会
2016/01/15 职场文书
MySQL 百万级数据的4种查询优化方式
2021/06/07 MySQL
解决Maven项目中 Invalid bound statement 无效的绑定问题
2021/06/15 Java/Android
Ajax实现异步加载数据
2021/11/17 Javascript
MySQL数据库配置信息查看与修改方法详解
2022/06/25 MySQL