Flask请求处理流程(request)[待续]
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)[待续]的更多相关文章
- Flask - 请求处理流程和上下文源码分析
目录 Flask - 请求处理流程和上下文 WSGI Flask的上下文对象及源码解析 0. 请求入口 1.请求上下文对象的创建 2. 将请求上下文和应用上下文入栈 3.根据请求的URl执行响应的视图 ...
- 笔记-flask-原理及请求处理流程
笔记-flask-原理及请求处理流程 1. 服务器声明及运行 最基本的flask项目代码如下 from flask import Flask app = Flask(__name__) @a ...
- Yii源码阅读笔记(二十一)——请求处理流程
Yii2请求处理流程: 首先:项目路径/web/index.php (new yii\web\Application($config))->run();//根据配置文件创建App实例,先实例化y ...
- Asp.Net构架(Http请求处理流程)、(Http Handler 介绍)、(HttpModule 介绍)
Asp.Net构架(Http请求处理流程) Http请求处理流程概述 对于普通访问者来说,这就像每天太阳东边升起西边落下一样是理所当然的:对于很多程序员来说,认为这个与己无关,不过是系统管理员或者网管 ...
- Http请求处理流程
本文结构: 一.HTTP请求处理流程的基础 1.网络分层 因特网TCP/IP分层模型共有五层:应用层.传输层.网络层.网络接口层和物理层.这种分层模型不同于OSI七层参考模型,但是,是实际使用中采用的 ...
- springmvc源码分析系列-请求处理流程
接上一篇-springmvc源码分析开头片 上一节主要说了一下springmvc与struts2的作为MVC中的C(controller)控制层的一些区别及两者在作为控制层方面的一些优缺点.今天就结合 ...
- 走进JavaWeb技术世界8:浅析Tomcat9请求处理流程与启动部署过程
谈谈 Tomcat 请求处理流程 转自:https://github.com/c-rainstorm/blog/blob/tomcat-request-process/reading-notes &l ...
- Webflux请求处理流程
spring mvc处理流程 在了解SpringMvc的请求流程源码之后,理解WebFlux就容易的多,毕竟WebFlux处理流程是模仿Servlet另起炉灶的. 下面是spring mvc的请求处理 ...
- 04-SpringMVC之请求处理流程
SpringMVC之请求处理流程 我们知道DispatcherServlet就是一个HttpServlet,而HttpServlet的请求就从doGet/doPost开始 DispatcherServ ...
随机推荐
- oracle增加表空间
select tablespace_name, sum(bytes)/1024/1024 from dba_data_files group by tablespace_name; select ta ...
- httpclient 连接路由
http路由 httpclient能够直接或通过路由建立连接到目标主机,这会涉及多个中间连接,也被称为跳. Httpclient区分路由和普通连接,通道和分层. 通道连接到目标主机的多个中间代理的使用 ...
- InstallShield中打包ArcEnineRuntime
InstallShield中打包ArcEnineRuntime 最近研究了一阵应用程序的打包,几天下来也算颇有收获.普通的.net程序打包相对简单一点,不过ArcEngine的应用程序还涉及到Engi ...
- Flappy Bird背后的故事
更多有价值的互联网文章:晓煦分享(http://www.ihuxu.com/share) 由越南游戏工程师阮哈东(Nguyen Ha Dong)开发的Flappy Bird这款游戏,画面不算精致且看起 ...
- 如何修改tomcat端口以及tomcat热部署
一.修改tomcat端口 1.首先我们需要知道,http的默认端口是80,tomcat的默认端口是8080,也就是说,如果我们将tomcat的默认端口号修改为80,输入网址的时候就可以不用输入端口了, ...
- 移动端 解决自适应 和 多种dpr (device pixel ratio) 的 [淘宝] 解决方案 lib-flexible
其实H5适配的方案有很多种,网上有关于这方面的教程也非常的多. 不管哪种方法,都有其自己的优势和劣势. 为什么推荐使用Flexible库来做H5页面的终端设备适配呢? 原理 简单易懂 源码疑问 ...
- css 兼容 各类手机的写法 待续
@media (device-height:480px) and (-webkit-min-device-pixel-ratio:2){/* 兼容iphone4/4s */ .class{} } @m ...
- SWIFT解析天气JSON格式
访问以下链接可以得到京城当天的天气:http://www.weather.com.cn/adat/sk/101010100.html 返回的JSON格式如下: {"weatherinfo&q ...
- 第十七篇 make的路径搜索综合实践
本节我们编写路径搜索相关的makefile,具体需求如下: 1.工程项目中不希望源码文件夹在编译时被改动(只读文件夹). 2.在编译时自动创建文件夹(build)用于存放编译结果. 3.编译过 ...
- 第十五篇 make中的隐式规则概述
前面我们讲到了makefile的依赖拆分的知识,现在可以引申出这样一个问题,如果同一个目标的不同命令拆分的写到不同地方会发生什么?下面我们给出程序和执行结果: 可见后面的命令会覆盖前面的命令, ...