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. Dos命令的介绍

    Dos命令的介绍目录 1.什么是Dos 2.Dos的简介 3.Dos命令有哪些 4.Dos命令下常见的错误信息 5.Config.sys文件的命令和配置 6.Dos自带的的批处理命令 1.什么是Dos ...

  2. SSO-基本概念

    什么是单点登录 单点登录(Single Sign On) 简称为sso,是目前流行的企业业务整合的解决方案之一.SSO的定义是在多个引用系统中用户只需要登录一次就可以访问所有相互信任的应用系统. 单点 ...

  3. sgu107. 987654321 problem 简单打表 难度:0

    107. 987654321 problem time limit per test: 0.25 sec. memory limit per test: 4096 KB For given numbe ...

  4. zoj3888

    题解: 维护比这个大的第二大 代码: #include<cstdio> #include<algorithm> #include<queue> #include&l ...

  5. Python Django 之 Model

    一.Model建表步骤 1. 2. 3. 二.单表的增删改查 1.insert 1)方法一 book.objects.create( title="疯狂外星人", price=10 ...

  6. LINUX文件格式化读写(文件指针,缓冲)

    body, table{font-family: 微软雅黑; font-size: 10pt} table{border-collapse: collapse; border: solid gray; ...

  7. express学习-express搭建后台

    前言:本文是纯用node express做一个后端服务的教程,并不等同于express官网的入门教程,本文也并不涉及任何高级的Node服务端性能优化等知识. 本文是在已经看过express官方入门指南 ...

  8. DevExpress v17.2新版亮点—WPF篇(六)

    用户界面套包DevExpress v17.2终于正式发布,本站将以连载的形式为大家介绍各版本新增内容.本文将介绍了DevExpress WPF v17.2 新的Hamburger Menu.Sched ...

  9. JavaScript事件简述

    事件简述 技术一般水平有限,有什么错的地方,望大家指正. 事件是我们平时经常使用,这次就来了解一下事件.首先我们要明确几个概念,JavaScript是单线程,浏览器是多线程的,并不是所有的事件处理函数 ...

  10. L224

    Astronomers have revealed details of mysterious signals emanating from a distant galaxy, picked up b ...