资讯专栏INFORMATION COLUMN

flask 核心 之 应用上下文 及 请求上下文

tinna / 650人阅读

摘要:的上下文对象有两种上下文,分别是请求上下文请求的对象,封装了请求的内容,生命周期请求处理完就结束了根据请求中的,重新载入该访问者相关的会话信息应用上下文处理请求时用作临时存储的对象。

Werkzeugs 是 Flask 的底层WSGI库。
什么是WSGI?

一段简单的app:

def dispath_request(self, request):
    return Response("Hello World!")

def wsgi_app(self, environ, start_response):
    request = Request(environ)
    response = self.dispath_request(request)
    return response(environ, start_response)

environ: 包含全部的HTTP请求信息的字典,由WSGI Server解包 HTTP 请求生成。
start_response:一个WSGI Server 提供的函数,调用可以返回相应的状态吗和HTTP报文头,函数在返回前必须调用一次。

Flask的上下文对象

Flask有两种Context(上下文),分别是

RequestContext 请求上下文;

Request 请求的对象,封装了Http请求(environ)的内容,生命周期请求处理完就结束了;

Session 根据请求中的cookie,重新载入该访问者相关的会话信息;

AppContext 应用上下文;

g 处理请求时用作临时存储的对象。每次请求都会重设这个变量, 生命周期请求处理完就结束了;

current_app 当前激活程序的程序实例,只要当前程序还在运行就不会失效。

flask 处理请求和响应的流程:

在 "flask/globals.py" 代码中:

# context locals
_request_ctx_stack = LocalStack()   
# LocalStack 是由werkzeug提供的栈结构类提供了push、pop等方法
# 并且Local对象是werkzeug开发的类似 thinking.local(用于隔离不同线程间的全局变量) 对象,实现了在同一个协程中数据的隔离和全局性,具体怎么实现看源代码,暂时没看明白
_app_ctx_stack = LocalStack()
current_app = LocalProxy(_find_app)
# partial(_lookup_req_object, "request") 总是返回 LocalStack 栈顶对象的 request 属性
# LocalProxy 用于代理Local对象和LocalStack对象,至于为什么使用代理。。
request = LocalProxy(partial(_lookup_req_object, "request"))
session = LocalProxy(partial(_lookup_req_object, "session"))
g = LocalProxy(partial(_lookup_app_object, "g"))

flask local https://www.jianshu.com/p/3f38b777a621
为什么要使用 LocalProxy 而不直接使用 Local 和 LocalStack

# AppContext 应用上下文
#
# 在flask/app.py下:
#
def app_context(self):       # self 就是app对象
        return AppContext(self)

#
# 在 `flask/ctx.py` 代码中
#
class AppContext(object):
    def __init__(self, app):
        self.app = app
        self.url_adapter = app.create_url_adapter(None)
        self.g = app.app_ctx_globals_class()
        # Like request context, app contexts can be pushed multiple times
        # but there a basic "refcount" is enough to track them.
        self._refcnt = 0

    def push(self):           # 这个方法就是把应用上下文push到LocalStack,AppContext类有__enter__方法
        """Binds the app context to the current context."""
        self._refcnt += 1
        if hasattr(sys, "exc_clear"):
            sys.exc_clear()
        _app_ctx_stack.push(self)
        appcontext_pushed.send(self.app)

#
# 在 flask/cli.py 中有
#
def with_appcontext(f):
    @click.pass_context
    def decorator(__ctx, *args, **kwargs):    # decorator 被装饰器后 _ctx 参数是 threading 的 local() 对象
        with __ctx.ensure_object(ScriptInfo).load_app().app_context():   # 在这里就把 应用上下文push到了LocalStack
            return __ctx.invoke(f, *args, **kwargs)
    return update_wrapper(decorator, f)

# RequestContext 请求上下文
#
#在flask/app.py下
#
def request_context(self, environ):     #  一次请求的环境变量
        return RequestContext(self, environ)

#
# 在flask/ctx.py下:
#
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
        self.url_adapter = app.create_url_adapter(self.request)
        self.flashes = None
        self.session = None
        ...
        ...
    def push(self):
          ...
          _request_ctx_stack.push(self)  

#
#在flask/app.py下
#
def wsgi_app(self, environ, start_response):              # 这里类似上面的那小段简单 Werkzeugs app
        ctx = self.request_context(environ)
        error = None
        try:
            try:
                ctx.push()
                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:
            if self.should_ignore_error(error):
                error = None
            ctx.auto_pop(error)
到此从 AppContext 到 RequestContext 梳理完 app.py 源码中有这么几个装饰器:

before_request(self, f) # 注册一个在请求到达前要执行的函数

before_first_request(self, f) # 注册在整个应用实例第一个请求到达前要执行的函数

after_request(self, f) # 注册一个在请求处理后的函数

teardown_request(self, f) # 注册在请求上下文栈弹出后要执行的函数

teardown_appcontext(self, f) # 注册在应用上下文结束时要执行的函数

context_processor(self, f) # 向模板上下文中注入变量和方法,这个f必须返回一个字典, 在渲染的模板中使用

url_defaults(self, f) # 为应用程序中的所有视图函数 注册URL值预处理器函数。 这些函数将在:before_request函数之前调用。

url_value_preprocessor(self, f) # 注册一个 ‘在请求的url匹配后视图函数执行前,把环境中的某些变量换个位置存储的’ 函数

# url_defaults(self, f) 和 url_value_preprocessor(self, f) 的使用
from flask import Blueprint, render_template
profile = Blueprint("profile", __name__, url_prefix="/")
@profile.url_defaults
def add_user_url_slug(endpoint, values):
    values.setdefault("user_url_slug", g.user_url_slug)
@profile.url_value_preprocessor
def pull_user_url_slug(endpoint, values):
    g.user_url_slug = values.pop("user_url_slug")
    query = User.query.filter_by(url_slug=g.user_url_slug)
    g.profile_owner = query.first_or_404()
@profile.route("/")
def timeline():
    return render_template("profile/timeline.html")
@profile.route("/photos")
def photos():
    return render_template("profile/photos.html")
@profile.route("/about")
def about():
    return render_template("profile/about.html")
方法:

dispatch_request(self): 匹配路由,返回视图函数或者错误处理函数的返回值,并且检测是否为option请求,如果是则构造默认的 ‘options response’ 响应。构造过程首先是 Request uri 所支持的方法集(get、post、等),然后更新 Responseallow 属性(set类型),最后返回Response对象,若不是option请求则执行视图函数;

make_response(self, rv): rv是视图函数的返回值,在python3中,rv可以使一个元组(body、status、headers)、Response类对象、 或者一个返回Response类对象的回调函数。这个函数的功能就是把视图函数返回的 status headers 绑定到 Response;

create_url_adapter(self, request):url_map 适配器。对werkzeug的Map的bindbind_to_environ两个方法进行了封装。bind: 绑定一个主机地址,并返回MapAdapter对象 ; bind_to_environ( 将MAP绑定到WSGI环境中,并返回MapAdapter对象(参数script_name在进行重定向时会用到);

try_trigger_before_first_request_functions(self):在该实例第一个请求到达时把当前线程加锁,然后依次执行被 before_first_request(self, f)装饰过得函数,然后释放锁并把_got_first_request置为True,再次就直接return;

preprocess_request(self) :该函数就是执行被url_value_preprocessorbefore_request装饰过的函数;

full_dispatch_request(self): 先执行try_trigger_before_first_request_functions,然后执行preprocess_request,若before_request中的函数有返回值则为其构造Response,然后跳过排在此函数后边的函数包括视图函数,若before_request的函数都返回的None或没有函数就执行 dispatch_request(self)

inject_url_defaults(self, error, endpoint, values): 执行被"url_defaults" 装饰的函数

process_response(self, response): 同preprocess_request(self)

通过查看源码了解到的几个工具:

click 包 把python代码打包成命令行工具
mimetypes 包 查看文件类型
itsdangerous 签名 序列化字符串

参考资料:
Flask的核心机制!关于请求处理流程和上下文
Werkzeug(Flask)之Local、LocalStack和LocalProxy

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/45028.html

相关文章

  • 从零开始搭建论坛(三):Flask框架简单介绍

    摘要:我们的论坛项目就使用了该框架。此外,麦子学院也有一个入门视频教程,一共小时的视频教程,涵盖开发的方方面面,包括环境的搭建,语法介绍,项目结构的组织,全球化,单元测试等内容。博客地址更多阅读的机制三个框架的对比 前面两篇文章中我们已经了解 Web(HTTP)服务器,Web应用程序,Web框架,WSGI这些 Python Web 开发中的概念。我们知道,Web框架通过将不同Web应用程序中...

    Alan 评论0 收藏0
  • FlaskWeb开发读书笔记jinja2模板

    摘要:注模板包含不存在此问题。在模板中,使用过滤器显式地标记一个字符串为安全的临时地完全禁用自动转义系统。通过在声明中直接添加或,当前的上下文可以传递到模板,而且不会自动禁用缓存。 官网:http://jinja.pocoo.org/docs/dev/中文文档:http://docs.jinkan.org/docs/j...当前版本2.8 安装:pip install Jinja2Flask...

    tangr206 评论0 收藏0
  • Flask表单

    摘要:通过的核心特性,函数可实现这种效果仅调用函数并不能把消息显示出来,程序使用的模板要渲染这些消息。注意在模板中使用循环是因为在之前的请求循环中每次调用函数时都会生成一个消息,所以可能有多个消息在排队等待显示。 五、表单 1、Flask-WTF 扩展 Flask-WTF 及其依赖可使用 pip 安装: (venv) $ pip install flask-wtf 2、跨站请求伪造保护 【设...

    justCoding 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<