07 Flask源码之:用户请求过来流程

1.创建ctx = RequestContext对象

  • RequestContext对象封装Request对象

  • RequestContext对象封装session数据

  • 源码实现:

    def wsgi_app(self, environ, start_response):
    """
    ctx = RequestContext(self, environ)
    """
    # 2.1 创建RequestContext对象
    ctx = self.request_context(environ)
    def request_context(self, environ):
    return RequestContext(self, environ)
    request_class = Request
    
    class RequestContext(object):
    def __init__(self, app, environ, request=None, session=None):
    self.app = app
    if request is None:
    """
    request_class = Request
    """
    request = app.request_class(environ)
    self.request = request
    self.session = session

2. 创建app_ctx = AppContext对象

  • AppContext对象封装App对象

  • AppContext对象封装g

  • 源码实现:

    def wsgi_app(self, environ, start_response):
    """
    ctx = RequestContext(self, environ)
    """
    # 2.1 创建RequestContext对象
    ctx = self.request_context(environ)
    error = None
    try:
    try:
    """
    2.2 执行ctx.push
    - app_ctx = 创建AppContext对象(app,g)
    - 将app_ctx放入local中
    - 将ctx放入到local中
    - session赋值
    - 路由匹配
    """
    ctx.push()
        def push(self):
    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 = AppContext(app)
    """
    # 创建appcontext对象
    app_ctx = self.app.app_context()
    def app_context(self):
    return AppContext(self)
    class AppContext(object):
    def __init__(self, app):
    self.app = app
    self.g = app.app_ctx_globals_class()

3. 将ctx对象、app_ctx对象放到local中

  • 然后ctx.push触发将 ctx对象,通过自己的LocalStack对象将其放入到Local中

  • 然后app_ctx.push触发将 app_ctx对象,通过自己的LocalStack对象将其放入到Local中

  • Local的本质是以线程ID为key,以{“stack”:[]}为value的字典

    存储结构:{

    1111:{“stack”:[ctx,]}

    };

    ​ {

    ​ 1111:{“stack”:[app_ctx,]}

    ​ }

  • 源码示例:

        def push(self):
    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 = AppContext(app)
    """
    # 创建appcontext对象
    app_ctx = self.app.app_context()
    # push将app_ctx放入到local中
    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()
    """
    self = ctx = RequestContext(self, environ)
    """
    # push将ctx放入到local中
    _request_ctx_stack.push(self) if self.session is None:
    session_interface = self.app.session_interface
    self.session = session_interface.open_session(self.app, self.request) if self.session is None:
    self.session = session_interface.make_null_session(self.app) if self.url_adapter is not None:
    # 路由匹配,将匹配到的endpoint放到request.url_rule中
    self.match_request()

4. 执行所有before_request函数以及所有的视图函数

  1. 执行full_dispatch_request函数
  2. 执行所有的before_request函数
  3. 执行视图函数
  • 源码示例:

    def wsgi_app(self, environ, start_response):
    """
    ctx = RequestContext(self, environ)
    """
    #2.1 创建RequestContext对象
    ctx = self.request_context(environ)
    error = None
    try:
    try:
    # 做了很多事
    """
    2.2 执行ctx.push
    - app_ctx = 创建AppContext对象(app,g)
    - 将app_ctx放入local中
    - 将ctx放入到local中
    - session赋值
    - 路由匹配
    """
    ctx.push()
    # 2.3 执行before_request/视图/after_request (处理session)
    response = self.full_dispatch_request()
    def full_dispatch_request(self):
    # 触发所有的before_first_request_funcs函数
    # 只在启动程序后,第一个请求到来时执行
    self.try_trigger_before_first_request_functions()
    try:
    # 信号,暂留
    request_started.send(self)
    # 执行before_request_funcs函数,如果有返回值就不执行视图函数了
    rv = self.preprocess_request()
    if rv is None:
    # 执行视图函数
    rv = self.dispatch_request()
    except Exception as e:
    rv = self.handle_user_exception(e)
    # 视图函数执行之后
    # 1.执行所有的after_request
    # 2.保存session
    return self.finalize_request(rv)

5. 执行所有after_request函数

  • session会加密返回给用户浏览器放到cookie中

  • 源码示例:

    def finalize_request(self, rv, from_error_handler=False):
    # 将rv视图函数返回值,封装到Reponse对象中
    response = self.make_response(rv)
    response = self.process_response(response)
    return response
    response_class = Response
    
    def make_response(self, rv):
    if not isinstance(rv, self.response_class):
    if isinstance(rv, (text_type, bytes, bytearray)):
    rv = self.response_class(rv, status=status, headers=headers)
    return rv
    def process_response(self, response):
    ctx = _request_ctx_stack.top
    funcs = ctx._after_request_functions
    # 执行所有的after_request_funcs
    funcs = chain(funcs, reversed(self.after_request_funcs[None]))
    for handler in funcs:
    response = handler(response)
    if not self.session_interface.is_null_session(ctx.session):
    # 保存session
    self.session_interface.save_session(self, ctx.session, response)
    return response

6. 销毁ctx和app_ctx

  • 如果请求结束不销毁ctx和app_ctx的话,会造成内存泄漏

  • 分别调用ctx和app_ctx的pop方法

  • 源码示例:

    def wsgi_app(self, environ, start_response):
    """
    ctx = RequestContext(self, environ)
    """
    #2.1 创建RequestContext对象
    ctx = self.request_context(environ)
    error = None
    try:
    try:
    """
    2.2 执行ctx.push
    - app_ctx = 创建AppContext对象(app,g)
    - 将app_ctx放入local中
    - 将ctx放入到local中
    - session赋值
    - 路由匹配
    """
    ctx.push()
    # 2.3 执行before_request/视图/after_request (处理session)
    response = self.full_dispatch_request()
    except Exception as e:
    error = e
    response = self.handle_exception(e)
    except: # noqa: B001
    error = sys.exc_info()[1]
    raise
    return response(environ, start_response)
    finally:
    if self.should_ignore_error(error):
    error = None
    # 2.4 销毁ctx/app_ctx
    ctx.auto_pop(error)
    def auto_pop(self, exc):
    self.pop(exc)
    def pop(self, exc=_sentinel):
    app_ctx = self._implicit_app_ctx_stack.pop()
    finally:
    rv = _request_ctx_stack.pop()
    if app_ctx is not None:
    app_ctx.pop(exc)

07 flask源码剖析之用户请求过来流程的更多相关文章

  1. flask源码剖析系列(系列目录)

    flask源码剖析系列(系列目录) 01 flask源码剖析之werkzurg 了解wsgi 02 flask源码剖析之flask快速使用 03 flask源码剖析之threading.local和高 ...

  2. 07 drf源码剖析之节流

    07 drf源码剖析之节流 目录 07 drf源码剖析之节流 1. 节流简述 2. 节流使用 3. 源码剖析 总结: 1. 节流简述 节流类似于权限,它确定是否应授权请求.节流指示临时状态,并用于控制 ...

  3. 08 Flask源码剖析之flask拓展点

    08 Flask源码剖析之flask拓展点 1. 信号(源码) 信号,是在flask框架中为我们预留的钩子,让我们可以进行一些自定义操作. pip3 install blinker 2. 根据flas ...

  4. Apache DolphinScheduler 源码剖析之 Worker 容错处理流程

    今天给大家带来的分享是 Apache DolphinScheduler 源码剖析之 Worker 容错处理流程 DolphinScheduler源码剖析之Worker容错处理流程 Worker容错流程 ...

  5. DolphinScheduler 源码剖析之 Master 容错处理流程

    点击上方蓝字关注 Apache DolphinScheduler Apache DolphinScheduler(incubating),简称"DS", 中文名 "海豚调 ...

  6. Flask源码剖析详解

    1. 前言 本文将基于flask 0.1版本(git checkout 8605cc3)来分析flask的实现,试图理清flask中的一些概念,加深读者对flask的理解,提高对flask的认识.从而 ...

  7. 04 flask源码剖析之LocalStack和Local对象实现栈的管理

    04 LocalStack和Local对象实现栈的管理 目录 04 LocalStack和Local对象实现栈的管理 1.源码入口 1. flask源码关于local的实现 2. flask源码关于l ...

  8. 06 flask源码剖析之路由加载

    06 Flask源码之:路由加载 目录 06 Flask源码之:路由加载 1.示例代码 2.路由加载源码分析 1.示例代码 from flask import Flask app = Flask(__ ...

  9. flask 源码剖析

    flask 上下文管理源码流程及涉及的部分技术点 [flask源码梳理]之一  偏函数_mro [flask源码梳理]之二  面向对象中__setattr__ [flask源码梳理]之三  Local ...

随机推荐

  1. netty实现消息中心(二)基于netty搭建一个聊天室

    前言 上篇博文(netty实现消息中心(一)思路整理 )大概说了下netty websocket消息中心的设计思路,这篇文章主要说说简化版的netty聊天室代码实现,支持群聊和点对点聊天. 此demo ...

  2. <VCC笔记> 溢出与unchecked

    在程序运算或者数据转换的时候,由于各种数据类型有各自的范围,运算的时候,其结果如果超出这个范围,就被称之为溢出.熟悉C#的同志们应该了解用来解决溢出(Overflow)问题的checked,unche ...

  3. Codeforces Round #647 (Div. 2)

    Problem A https://codeforces.com/contest/1362/problem/A 判断x/y是不是2的k次方, 如果是 k/3 + (k%3)/2 + (k%3%2)即为 ...

  4. xdoj 2020校赛复盘

    平时写东西都不喜欢复盘,这肯定不是一个好习惯,感觉每次花好几个小时甚至好几天写题目然后没写出来也不去看题解是一种很蠢的行为( 花了这么久时间打校赛,虽然水平很low,数据结构也不太会用,还是记录一下自 ...

  5. 色彩空间转换 rgb转ycbcr422/ycbcr422转rgb

    在图像处理过程中通常需要会对图像类型进行互相转换,在此给出两种转换的工程代码. 1.在将ycbCr422转rgb时,通常先将ycbcr422转换成ycbcr444再讲ycbcr444转成rgb 1.1 ...

  6. git添加所有新文件

    git add -A 提交所有变化 git add -u 提交被修改(modified)和被删除(deleted)文件,不包括新文件(new) git add . 提交新文件(new)和被修改(mod ...

  7. web api(基于NFine框架) 中接口跳转数据格式错误解决方案

    using NFine.Code; using NFine.Domain; using System.Web.Http; using Newtonsoft.Json; namespace Api.Co ...

  8. swiper 实现滑动解锁

    最近项目中有这样一个需求,研究了两种写法一个原生,一个使用框架 原生写法: <!DOCTYPE html> <html> <head> <meta chars ...

  9. RockeMQ安装与入门

    淘宝内部的交易系统使用了淘宝自主研发的Notify消息中间件,使用Mysql作为消息存储媒介,可完全水平扩容,为了进一步降低成本,淘宝开发团队认为存储部分可以进一步优化,2011年初,Linkin开源 ...

  10. IDEA+Maven+Tomcat构建Web项目的三种方法

    [本文版权归微信公众号"代码艺术"(ID:onblog)所有,若是转载请务必保留本段原创声明,违者必究.若是文章有不足之处,欢迎关注微信公众号私信与我进行交流!] 本文将介绍三种方 ...