flask请求和应用上下文

关于WSGI

WSGI(全称Web Server Gateway Interface),是为 Python 语言定义的Web服务器Web应用程序之间的一种简单而通用的接口,它封装了接受HTTP请求解析HTTP请求发送HTTP响应等等的这些底层的代码和操作,使开发者可以高效的编写Web应用。

一个简单的使用WSGI的App例子:

def application(environ, start_response):
    start_response(‘200 OK‘, [(‘Content-Type‘, ‘text/html‘)])
    return [b‘<h1>Hello, I Am WSGI!</h1>‘]
  • environ: 一个包含全部HTTP请求信息的字典,由WSGI Server解包HTTP请求生成。
  • start_response: 一个WSGI Server提供的函数,调用可以发送响应的状态码和HTTP报文头, 函数在返回前必须调用一次start_response()
  • application()应当返回一个可以迭代的对象(HTTP正文)。
  • application()函数由WSGI Server直接调用和提供参数。
  • Python内置了一个WSGIREFWSGI Server,不过性能不是很好,一般只用在开发环境。可以选择其他的如Gunicorn

WSGI Server 和 App交互图

Flask的上下文对象

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

  • RequestContext 请求上下文
  • Request 请求的对象,封装了Http请求(environ)的内容
  • Session 根据请求中的cookie,重新载入该访问者相关的会话信息。
  • AppContext 程序上下文
  • g 处理请求时用作临时存储的对象。每次请求都会重设这个变量
  • current_app 当前激活程序的程序实例

生命周期:

  • current_app的生命周期最长,只要当前程序实例还在运行,都不会失效。
  • Requestg的生命周期为一次请求期间,当请求处理完成后,生命周期也就完结了
  • Session就是传统意义上的session了。只要它还未失效(用户未关闭浏览器、没有超过设定的失效时间),那么不同的请求会共用同样的session。

Flask处理流程

Flask处理请求流程

第一步:创建上下文

Flask根据WSGI Server封装的请求等的信息(environ)新建RequestContext对象AppContext对象

# 声明对象
# LocalStack  LocalProxy 都由Werkzeug提供
# 我们不深究他的细节,那又是另外一个故事了,我们只需知道他的作用就行了
# LocalStack 是栈结构,可以将对象推入、弹出
# 也可以快速拿到栈顶对象。当然,所有的修改都只在本线程可见。
_request_ctx_stack = LocalStack()
_app_ctx_stack = LocalStack()
# 如果调用一个LocalStack实例, 能返回一个 LocalProxy 对象
# 这个对象始终指向 这个LocalStack实例的栈顶元素。
# 如果栈顶元素不存在,访问这个 LocalProxy 的时候会抛出 RuntimeError异常
# LocalProxy对象你只需暂时理解为栈里面的元素即可了
current_app = LocalProxy(_find_app)
request = LocalProxy(partial(_lookup_req_object, ‘request‘))
session = LocalProxy(partial(_lookup_req_object, ‘session‘))
g = LocalProxy(partial(_lookup_app_object, ‘g‘))
# 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
        self.url_adapter = app.create_url_adapter(self.request)
        self.flashes = None
        self.session = None
#AppContext
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()
        self._refcnt = 0

这里需要注意的是,RequestContext在初始化的时候,当前Flask的实例作为参数被传进来。虽然每次的请求处理都会创建一个RequestContext对象,但是每一次传入的app参数却是同一个。通过这个机制,可以使得:

由同一个Flask实例所创建的RequestContext,其成员变量app都是同一个Flask实例对象 。实现了多个RequestContext对应同一个current_app 的目的。

第二步:入栈

RequestContext对象push进_request_ctx_stack里面。在这次请求期间,访问request对象session对象将指向这个栈的栈顶元素

class RequestContext(object):
    def push(self):
        ....
        _app_ctx_stack.push(self)
        appcontext_pushed.send(self.app)

AppContext对象push进_app_ctx_stack里面。在这次请求期间,访问g对象将指向这个栈的栈顶元素

class AppContext(object):
    def push(self):
        ....
        _request_ctx_stack.push(self)

第三步:请求分发

response = self.full_dispatch_request()
Flask将调用full_dispatch_request函数进行请求的分发,之所以不用给参数,是因为我们可以通过request对象获得这次请求的信息。full_dispatch_request将根据请求的url找到对应的蓝本里面的视图函数,并生成一个response对象。注意的是,在请求之外的时间,访问request对象是无效的,因为request对象依赖请求期间的_request_ctx_stack栈。

第四步:上下文对象出栈

这次HTTP的响应已经生成了,就不需要两个上下文对象了。分别将两个上下文对象出栈,为下一次的HTTP请求做出准备。

第五步:响应WSGI

调用Response对象,向WSGI Server返回其结果作为HTTP正文。Response对象是一个 可调用对象,当调用发生时,将首先执行WSGI服务器传入的start_response()函数 发送状态码和HTTP报文头。

最后附上Flask处理请求的wsgi_app函数

# environ: WSGI Server封装的HTTP请求信息
# start_response: WSGI Server提供的函数,调用可以发送状态码和HTTP报文头
def wsgi_app(self, environ, start_response):
    # 根据environ创建上下文
    ctx = self.request_context(environ)
    # 把当前的request context,app context绑定到当前的context
    ctx.push()
    error = None
    try:
        try:
            #根据请求的URL,分发请求,经过视图函数处理后返回响应对象
            response = self.full_dispatch_request()
        except Exception as e:
            error = e
            response = self.make_response(self.handle_exception(e))
        return response(environ, start_response)
    finally:
        if self.should_ignore_error(error):
          error = None
       # 最后出栈
        ctx.auto_pop(error)

原文地址:https://www.cnblogs.com/yidashi110/p/10467881.html

时间: 2024-11-06 11:41:43

flask请求和应用上下文的相关文章

flask 请求上下文源码(转)

本篇阅读目录 一.flask请求上下文源码解读 二.http聊天室(单聊/群聊)- 基于gevent-websocket 回到顶部 转:https://www.cnblogs.com/li-li/p/10252058.html#main 一.flask请求上下文源码解读 通过上篇源码分析,我们知道了有请求发来的时候就执行了app(Flask的实例化对象)的__call__方法,而__call__方法返回了app的wsgi_app(environ, start_response)方法的执行结果,而

Flask请求-响应循环

Flask请求-响应循环 要想让视图函数能够请求对象,一种直接了当的方式是将其作为参数闯入试图函数,不过这会导致应用中的每个视图函数,不过这会导致每个视图函数都多出一个参数.除了访问请求对象,如果视图函数在处理请求时还要访问其他对象,情况会变得更糟. 为了避免大量可有可无的参数把视图函数弄的一团糟,Flask使用上下文 from flask import Flask @app.route("/") def index(): user_agent = request.header.get

确保一个请求共用一个上下文

//职责:确保一个请求共用一个上下文 public class DataBaseContextFactory { //返回基类,这样就可以随意做到切换上下文了 public static DbContext GetDataTabelContext() { //共用一个请求上下文实例 DbContext dbcontext = CallContext.GetData("DbContext") as DbContext; if (dbcontext == null) { dbcontext

Flask 请求扩展

Flask 请求扩展 from flask import Flask,request app = Flask(__name__) before_request # 用before_request装饰的函数是执行响应函数之前执行, # 如果有多个before_request他会自上而下按照顺序执行 # 如果before_request有返回值,那后面的before_request都不会执行,且响应函数也不会执行 # 其有没有返回值都不会影响after_request的执行 @app.before_

flask请求上下文 (转)

本篇阅读目录 一.flask中的CBV 二.werkzeug + 上下文初步解读 三.偏函数和线程安全 回到顶部 一.flask中的CBV 对比django中的CBV,我们来看一下flask中的CBV怎么实现? from flask import Flask, render_template, url_for, views app = Flask(__name__) class Login(views.MethodView): def get(self): print(url_for("my_l

Flask请求上下文request

原文地址:https://www.cnblogs.com/omak/p/9911750.html

flask请求流程

启动先执行manage.py 中的    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, **optio

在Owin Self-Hosing下实现每个请求中共享上下文(数据)

问题 这几天在做公司的外部WebApi网关,由于使用了OAuth2.0,所以不得不使用Owin来部署网关. 而涉及到请求上下文的问题,为了使逻辑层能获取到请求头的信息,又不与网管耦合,决定把请求信息写到一个访问上下文中. 在传统的Http部署下,这个问题很好解决,我们可以使用HttpContext或者CallContext都是可行的. 然而在Owin的Self-Hosting情况下,这两者都无法使用. 在网上检索了一下,找到了很完善的解决方法.记录分享一下. 解决方案 方法一: 使用OwinCo

Flask 请求源码分析

执行app.run()方法: def run(self, host=None, port=None, debug=None, **options): from werkzeug.serving import run_simple if host is None: host = '127.0.0.1' if port is None: server_name = self.config['SERVER_NAME'] if server_name and ':' in server_name: po