美文网首页python学习Flask首页投稿(暂停使用,暂停投稿)
Flask的核心机制!关于请求处理流程和上下文

Flask的核心机制!关于请求处理流程和上下文

作者: ayawaya | 来源:发表于2016-09-05 23:18 被阅读4267次

    学习一样东西不能只停留在表面,我们要探索其中的细节,学习作者的编程思想,这样才能更进一步。

    关于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)
    

    参考资料
    汇智网:Flask框架-上下文对象 :Flask核心机制

    相关文章

      网友评论

      • SeaMonster:看了文章,请教2个问题
        1 LocalStack, LocalProxy 的作用是什么? flask如何保存(区分)每次请求的request context?
        2 request context,application context为什么要用栈的形式?
      • 阿渊__:总结得很好,理解了很多内容:+1:
      • AllenBigBear:谢谢博主的文章

      本文标题:Flask的核心机制!关于请求处理流程和上下文

      本文链接:https://www.haomeiwen.com/subject/cgwrettx.html