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. Java学习之多线程详解

    一.多线程的实现 1.继承Thread类 ​ a.子类继承Thread类具备多线程能力 ​ b.启动线程:子类对象.start() ​ c.不建议使用:避免OOP单继承局限性 package com. ...

  2. @gym - 100958J@ Hyperrectangle

    目录 @description@ @solution@ @accepted code@ @details@ @description@ 给定一个大小为 \(l_1\times l_2 \dots l_ ...

  3. FR6安装问题备注

    好久以前偶尔用用FR,采用安装执行文件的方式(5.3版安装没问题).及其编译包的方式都没有问题,最近在6.x提示如下(fr6_5_11_all_ent等),不知是系统原因还是文件问题,未解: ---- ...

  4. 基于docker-compose搭建gitlab

    安装及配置 修改docker-compose文件 vim docker-compose.yml gitlab: image: 'gitlab/gitlab-ce:latest' restart: al ...

  5. OO第二单元——兜兜转转之神秘电梯

    一.设计策略及程序结构分析 1.第一次作业 第一次作业是需要我们用多线程模拟一个实时电梯系统,功能比较简单正常,但要有捎带功能,我采用的调度策略便是指导书上提供的ALS调度策略,采用消费者-生产者模式 ...

  6. Vmware的各版本KEY

    算是之前收集到的备份一下在这里吧,顺便方便别人(ô‿ô) 应该是比较全的 VMware Workstation4.xx for WindowsZHDH1-UR90N-W844G-4PTN6G1NP0- ...

  7. yum 安装包的时候提示“没有可用软件包”

    今天在使用 yum 命令进行包的下载时候,Linux 提示 没有可用的软件包~ 如下: [root@localhost share]# yum -y install wordpress 已加载插件:f ...

  8. js清除所有的空格

    /** * 清除所有的空格 * @returns {*} */ String.prototype.removeSpace = function () { var str = this.replaceA ...

  9. 看完这篇 HashMap,和面试官扯皮就没问题了

    HashMap 概述 如果你没有时间细抠本文,可以直接看 HashMap 概述,能让你对 HashMap 有个大致的了解. HashMap 是 Map 接口的实现,HashMap 允许空的 key-v ...

  10. [ APUE ] 第三章 文件系统

    1. 文件描述符 打开或创建一个文件时,内核向进程返回一个文件描述符,当读.写一个文件时,用open()或creat()返回的文件描述符标识该文件,将其作为参数传递给write.read. stdin ...