【Flask学习笔记:四】Flask 应用和请求上下文

目录:
  【Flask学习笔记:一】开发环境部署
  【Flask学习笔记:二】Flask 入门基础知识
  【Flask学习笔记:三】Flask 中的 request、response
  【Flask学习笔记:四】Flask 应用和请求上下文
  【Flask学习笔记:五】Jinja2 模板引擎
  【Flask学习笔记:六】Flask 蓝图
  【Flask学习笔记:七】Flask - WTF 处理表单
  【Flask学习笔记:八】Flask 中的 cookie、session
  【Flask学习笔记:九】Flask-SQLAlchemy

【Flask学习笔记:四】Flask 应用和请求上下文

  Flask 从客户端收到请求时,要让视图函数能访问一些对象,这样才能处理请求。请求对象就是一个很好的例子,它封装了客户端发送的 HTTP 请求。
  要想让视图函数能够访问请求对象,一种直接了当的方式是将其作为参数传入视图函数,不过这会导致应用中的每个视图函数都多出一个参数。除了访问请求对象,如果视图函数在处理请求时还要访问其他对象,情况会变得更糟。
  为了避免大量可有可无的参数把视图函数弄得一团糟, Flask 使用上下文临时把某些对象变为全局可访问。

有了上下文,便可以像下面这样编写视图函数:

from flask import request

@app.route('/')
def index():
	user_agent = request.headers.get('User-Agent')
	return '<p>Your browser is {}</p>'.format(user_agent)

  在这个视图函数中我们把 request 当作全局变量使用。事实上,request 不可能是全局变量。在多线程服务器中,多个线程同时处理不同客户端发送的不同请求时,每个线程看到的 request 对象必然不同。Flask 使用上下文让特定的变量在一个线程中全局可访问,与此同时不会干扰其他线程。

在 Flask 中有两种上下文:应用上下文请求上下文

变量名上下文说明
current_app应用上下文当前应用的应用实例
g应用上下文处理请求时用作临时存储的对象,每次请求都会重设这个变量
request请求上下文请求对象,封装了客户端发出的 HTTP 请求中的内容
session请求上下文用户会话,值为一个字典,存储请求之间需要 ‘记住’ 的值

Flask 在发送请求之前推送应用和请求上下文,请求处理完成后再将其删除。应用上下文被推送后,就可以在当前线程中使用 current_app 和 g 变量。类似的,请求上下文被推送后,就可以使用 request 和 session 变量。如果使用这些变量时没有激活应用上下文或请求上下文,就会导致错误。

简单的画一个原理图:
在这里插入图片描述
1.接收到一个请求时,Flask 会先执行 wsgi_app 这个函数对象

    def __call__(self, environ, start_response):
        """Shortcut for :attr:`wsgi_app`."""
        return self.wsgi_app(environ, start_response)

2.实例化一个 RequestContext 对象,存储了此次请求的一些信息,

    def wsgi_app(self, environ, start_response):
        ctx = self.request_context(environ)
        ctx.push()
        error = None
        try:
            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:
            if self.should_ignore_error(error):
                error = None
            ctx.auto_pop(error)

3.将实例化的 RequestContext 推送到 _request_ctx_stack 中, 在此之前 Flask 会检查 _app_ctx_stack 这个栈顶是否存在 AppContext,不存在则实例化了一个 AppContext 推到 _app_ctx_stack 中,然后才将 RequestContext 推入到 _request_ctx_stack 中

def push(self):
	top = _request_ctx_stack.top
    if top is not None and top.preserved:
         top.pop(top._preserved_exc)

     # Before we push the request context we have to ensure that there
     # is an application context.
     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()

     _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()

4.最后将存储的ctx对象pop掉,整个请求结束。

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)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值