WSGI简介

WSGI(全称Web Server Gateway Interface),是为 Python 语言定义的Web服务器和Web应用程序之间的一种简单而通用的接口,它封装了接受HTTP请求、解析HTTP请求、发送HTTP,响应等等的这些底层的代码和操作,使开发者可以高效的编写Web应用。

Flask的WSGI工具箱采用Werkzeug。工作原理如下:

from werkzeug.wrappers import Request, Response
from werkzeug.serving import run_simple

@Request.application
def hello(request):
    return Response('Hello World!')

if __name__ == '__main__':
    run_simple('localhost', 4000, hello)

一个最简单的Flask的Hello World示例:

from flask import Flask, request

app = Flask(__name__)

@app.route('/')
def index():
    if request.method == 'POST':
        return 'Hello World!'

if __name__ == '__main__':
    app.run()

启动先执行: app.run()

class Flask(_PackageBoundObject):

def run(self, host=None, port=None, debug=None, **options):
      from werkzeug.serving import run_simple
      try:
          # run_simple 是werkzeug 提供的方法,会执行第三个参数 self()
          run_simple(host, port, self, **options)

执行app(),对象()表示调用对象的__call__方法

class Flask(_PackageBoundObject):
   def __call__(self, environ, start_response):
        return self.wsgi_app(environ, start_response)

又调用了app.wsgi_app方法

def wsgi_app(self, environ, start_response):
    # 1.
    ctx = self.request_context(environ)
    # 2
    ctx.push()
    error = None
    try:
        # 3
        try:
            response = self.full_dispatch_request()
        except Exception as e:
            error = e
            response = self.handle_exception(e)
        except:
            error = sys.exc_info()[1]
            raise
        return response(environ, start_response)
    finally:
        # 4.
        if self.should_ignore_error(error):
            error = None
        ctx.auto_pop(error)

第1步:执行app.request_context方法,把请求的相关信息传进去了

class Flask(_PackageBoundObject):
   def request_context(self, environ):
        return RequestContext(self, environ)

返回了一个RequestContext类的实例对象

class RequestContext(object):
    def __init__(self, app, environ, request=None):
        self.app = app
        if request is None:
            request = app.request_class(environ)
        self.request = request

request_class = Request ,class Request(BaseRequest...)

class BaseRequest(object):
    def __init__(self, environ, populate_request=True, shallow=False):
        self.environ = environ

在RequestContext的__init__()方法中,app又调用了request_class方法,也就是Request 实例一个对象,Request又继承了RequestBase而,也就是在这里封装了请求进来的所有数据environ等。

第2步:执行ctx.push()方法

因为ctx是RequestContext类的对象,那我们就要去RequestContext类中找push方法

class RequestContext(object):
    def push(self):
        # 2.1
        top = _request_ctx_stack.top
        if top is not None and top.preserved:
            top.pop(top._preserved_exc)
        app_ctx = _app_ctx_stack.top
        if app_ctx is None or app_ctx.app != self.app:
            app_ctx = self.app.app_context()
            app_ctx.push()
            self._implicit_app_ctx_stack.append(app_ctx)
        else:
            self._implicit_app_ctx_stack.append(None)

        if hasattr(sys, 'exc_clear'):
            sys.exc_clear()
        # 2.2
        _request_ctx_stack.push(self)
        self.session = self.app.open_session(self.request)
        if self.session is None:
            self.session = self.app.make_null_session()

2.1步,其中:_request_ctx_stack = LocalStack() ,_request_ctx_stack是LocalStack类的实例化对象,是一个全局对象。这一步,到_app_ctx_stack这个栈中取最后一个数据,没有就返回None

class LocalStack(object):
    def __init__(self):
        self._local = Local()
    @property
    def top(self):
        try:
            return self._local.stack[-1]
        except (AttributeError, IndexError):
            return None

2.2步,_request_ctx_stack.push(self)将封装了请求数据的LocalStack的对象封装到列表中。

class LocalStack(object):
    def __init__(self):
        self._local = Local()
    def push(self, obj):
        """Pushes a new item to the stack"""
        rv = getattr(self._local, 'stack', None)
        if rv is None:
            self._local.stack = rv = []
        rv.append(obj)
        return rv

第3步:app.full_dispatch_request()   执行视图函数 时执行。此处后面再谈论,先进行下一步

第4步,执行了RequestContext 的 pop 方法

class RequestContext(object):
    def auto_pop(self, exc):
        else:
            self.pop(exc)

pop

class RequestContext(object):
    def pop(self, exc=_sentinel):
        app_ctx = self._implicit_app_ctx_stack.pop()

        try:
            clear_request = False
            if not self._implicit_app_ctx_stack:
                self.preserved = False
                self._preserved_exc = None
                if exc is _sentinel:
                    exc = sys.exc_info()[1]
                self.app.do_teardown_request(exc)
                if hasattr(sys, 'exc_clear'):
                    sys.exc_clear()

                request_close = getattr(self.request, 'close', None)
                if request_close is not None:
                    request_close()
                clear_request = True
        finally:
            rv = _request_ctx_stack.pop()

            if clear_request:
                rv.request.environ['werkzeug.request'] = None

            if app_ctx is not None:
                app_ctx.pop(exc)

            assert rv is self, 'Popped wrong request context.  ' \
                '(%r instead of %r)' % (rv, self)

实则执行的是LocalStack对象的pop方法移除索引为-1的元素:

class LocalStack(object):
    def __init__(self):
        self._local = Local()
    def pop(self):
        stack = getattr(self._local, 'stack', None)
        if stack is None:
            return None
        elif len(stack) == 1:
            release_local(self._local)
            return stack[-1]
        else:
            return stack.pop()  

第3步,我们在试图函数中使用request的时候,发先request是一个全局变量。

request = LocalProxy(partial(_lookup_req_object, 'request'))

其中LocalProxy的参数是一个偏函数,request作为参数,传入_lookup_req_object函数中。通过上下文管理器的原理,为每个线程创建独有的空间,进行request的封装。根据视图函数中的请求方式或者请求内容执行对应的方法,如在函数中print(request)将出发LocalProxy类对象的__str__()方法:

__str__ = lambda x: str(x._get_current_object())

其中name就是request

@implements_bool
class LocalProxy(object):
    __slots__ = ('__local', '__dict__', '__name__', '__wrapped__')

    def __init__(self, local, name=None):
        object.__setattr__(self, '_LocalProxy__local', local)
        object.__setattr__(self, '__name__', name)
        if callable(local) and not hasattr(local, '__release_local__'):
            object.__setattr__(self, '__wrapped__', local)

    def _get_current_object(self):
        if not hasattr(self.__local, '__release_local__'):
            return self.__local()
        try:
            return getattr(self.__local, self.__name__)
        except AttributeError:
            raise RuntimeError('no object bound to %s' % self.__name__)

关于上下文,线程协程的相关文章之前整理的如下:

点击跳转

Flask请求处理流程(request)[待续]的更多相关文章

  1. Flask - 请求处理流程和上下文源码分析

    目录 Flask - 请求处理流程和上下文 WSGI Flask的上下文对象及源码解析 0. 请求入口 1.请求上下文对象的创建 2. 将请求上下文和应用上下文入栈 3.根据请求的URl执行响应的视图 ...

  2. 笔记-flask-原理及请求处理流程

    笔记-flask-原理及请求处理流程 1.      服务器声明及运行 最基本的flask项目代码如下 from flask import Flask app = Flask(__name__) @a ...

  3. Yii源码阅读笔记(二十一)——请求处理流程

    Yii2请求处理流程: 首先:项目路径/web/index.php (new yii\web\Application($config))->run();//根据配置文件创建App实例,先实例化y ...

  4. Asp.Net构架(Http请求处理流程)、(Http Handler 介绍)、(HttpModule 介绍)

    Asp.Net构架(Http请求处理流程) Http请求处理流程概述 对于普通访问者来说,这就像每天太阳东边升起西边落下一样是理所当然的:对于很多程序员来说,认为这个与己无关,不过是系统管理员或者网管 ...

  5. Http请求处理流程

    本文结构: 一.HTTP请求处理流程的基础 1.网络分层 因特网TCP/IP分层模型共有五层:应用层.传输层.网络层.网络接口层和物理层.这种分层模型不同于OSI七层参考模型,但是,是实际使用中采用的 ...

  6. springmvc源码分析系列-请求处理流程

    接上一篇-springmvc源码分析开头片 上一节主要说了一下springmvc与struts2的作为MVC中的C(controller)控制层的一些区别及两者在作为控制层方面的一些优缺点.今天就结合 ...

  7. 走进JavaWeb技术世界8:浅析Tomcat9请求处理流程与启动部署过程

    谈谈 Tomcat 请求处理流程 转自:https://github.com/c-rainstorm/blog/blob/tomcat-request-process/reading-notes &l ...

  8. Webflux请求处理流程

    spring mvc处理流程 在了解SpringMvc的请求流程源码之后,理解WebFlux就容易的多,毕竟WebFlux处理流程是模仿Servlet另起炉灶的. 下面是spring mvc的请求处理 ...

  9. 04-SpringMVC之请求处理流程

    SpringMVC之请求处理流程 我们知道DispatcherServlet就是一个HttpServlet,而HttpServlet的请求就从doGet/doPost开始 DispatcherServ ...

随机推荐

  1. spring boot 学习(六)spring boot 各版本中使用 log4j2 记录日志

    spring boot 各版本中使用 log4j2 记录日志 前言 Spring Boot中默认日志工具是 logback,只不过我不太喜欢 logback.为了更好支持 spring boot 框架 ...

  2. golang模拟动态高优先权优先调度算法

    实验二  动态高优先权优先调度 实验内容 模拟实现动态高优先权优先(若数值越大优先权越高,每运行一个时间单位优先权-n,若数值越小优先权越高,没运行一个时间单位优先权+n),具体如下: 设置进程体:进 ...

  3. 编译EXE文件的时候pcap编译不进去。 pyinstaller pcap pypcap 报错

    如果生成的exe源码中有import pcap 那么你目标机上就要先装npcap 并勾选winpcap API. 然后就不出这个问题了. 暂时的办法是第一个exe不包含import pcap.自检np ...

  4. Tushare安装

    在python中安装tushare踩坑记录一下: 安装 lxml requests bs4 pandas 不然一直提示错误.

  5. Java实现冒泡排序、折半查找

    1.冒泡排序 public class BubbleSort{ public static void main(String[] args){ int score[] = {67, 69, 75, 8 ...

  6. L1-008 求整数段和

    给定两个整数A和B,输出从A到B的所有整数以及这些数的和. 输入格式: 输入在一行中给出2个整数A和B,其中−,其间以空格分隔. 输出格式: 首先顺序输出从A到B的所有整数,每5个数字占一行,每个数字 ...

  7. MyEclipse WebSphere开发教程:WebSphere 7安装指南(二)

    [周年庆]MyEclipse个人授权 折扣低至冰点!立即开抢>> [MyEclipse最新版下载] 三.禁用Windows系统服务 默认情况下,当安装WebSphere Applicati ...

  8. redis 五大数据类型之string篇

    一: 1:string(字符串) string是redis最基本的类型,你可以理解成与Memcached一模一样的类型,一个key对应一个value. string类型是二进制安全的.意思是redis ...

  9. css 和 UI 框架 ---------- vue 待续

    vue 框架组件集合 radon-UI: 他人评价 --  一个帮助你快速开发产品的Vue组件库,简洁好用,效率高,让你摆脱各种定制化的烦恼. 个人感觉 --   组件简洁,  功能全面,  文档清晰 ...

  10. mysql-5.6.17-win32安装

    下载免安装压缩文件http://dev.mysql.com/downloads/mysql/ 解压到自定义目录,我这里演示的是D:\wamp\mysql\   复制根目录下的my-default.in ...