Flask---上下文源码分析

[toc]
这里写图片描述

我之前在Flask—werkzeug请求、响应源码分析中简单分析过werkzeug的请求、响应简单流程,这篇笔记就从Flask的角度,去分析下session的源码
首先我们先从一个用例入手

用例


from flask import Flask,session
app = Flask(__name__)
app.secret_key = 'sadfasdfasdf'

@app.route('/x1')
def index():
    session['k1'] = 123
    return "Index"


@app.route('/x2')
def order():
    print(session.get("k2"))
    return "Order"

if __name__ == '__main__':
    app.run()

源码分析

首先从该行代码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:
                port = int(server_name.rsplit(':', 1)[1])
            else:
                port = 5000
        if debug is not None:
            self.debug = bool(debug)
        options.setdefault('use_reloader', self.debug)
        options.setdefault('use_debugger', self.debug)
        try:
            run_simple(host, port, self, **options)
        finally:
            self._got_first_request = False

首先看run方法的参数def run(self, host=None, port=None, debug=None, **options)
主机、端口进行设置,options其他的参数(诸如processes、threaded、ssl_context等),然后去执行run_simple(host, port, self, **options)
我在文章开始贴出那篇博客,已经了解过werkzeug的执行流程,这里就简单说下
run_simple方法中,通过判断去执行inner()方法,然后就srv.serve_forever()将服务运行起来

wsgi有两方,服务器方 和 应用程序
①服务器方:其调用应用程序,给应用程序提供(环境信息)和(回调函数), 这个回调函数是用来将应用程序设置的http header和status等信息传递给服务器方.

②应用程序:用来生成返回的header,body和status,以便返回给服务器方
我们在看下run_simple(host, port, self, **options)代码,这个self就是我们示例中的app实例对象

app = Flask(__name__)
if __name__ == '__main__':
    app.run()

根据wsgi,当请求到来时,wsgi服务会去回调该对象(当然也可以是可回掉函数),我们知道在调用app实例时,会调用该实例对象的__call__方法,那我们就来看下该方法

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

其调用应用程序,给应用程序提供(环境信息)和(回调函数), 这个回调函数是用来将应用程序设置的http header和status等信息传递给服务器方.
我们继续看下wsgi_app方法


    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)

在上述方法代码ctx = self.request_context(environ)中,看看对请求环境参数做了哪些处理呢?

    def request_context(self, environ):
        return RequestContext(self, environ)

继续跟踪方法return RequestContext(self, environ),我们来看下通过environ是如何构造RequestContext的,在__init__中参看如下代码

 request = app.request_class(environ)

然后上述一行代码定位到request_class = Request,我们可以判断出app.request_class(environ)所以是构造了一个Request实例对象,将environ参数传递过去,
接下来我们就看下Request类,该类如下

class Request(RequestBase):

该类继承RequestBase,当我们跟踪代码查看父类RequestBase时,定位到如下代码,咦?奇怪为什么不是RequestBase类,这是因为在flask\wrappers.py模块中,做了命名处理
from werkzeug.wrappers import Request as RequestBase, Response as ResponseBase,所以是继承了werkzeug\wrappers.py下的Request


class Request(BaseRequest, AcceptMixin, ETagRequestMixin,
              UserAgentMixin, AuthorizationMixin,
              CommonRequestDescriptorsMixin):

Request是个多重继承类,以下是继承类的作用:
AcceptMixin类:实现了请求内容协商的部分,比如请求接受的语言、编码格式、相应内容等
ETagRequestMixin类:将实体标记和缓存描述符添加到请求对象或对象中,也提供对缓存控制头的访问
UserAgentMixin类:向请求对象添加一个“UsRoAgent”属性,该对象包含作为请求触发浏览器的解析的用户代理。
AuthorizationMixin类:添加属性,代表请求中的HTTP_AUTHORIZATION值
CommonRequestDescriptorsMixin类:请求对象混合这个类将自动获得一对夫妇的描述符具有自动类型转换的HTTP报头

我们就来主要看下BaseRequest类,以下是其初始方法,将environ进行赋值在BaseRequest进行封装

 def __init__(self, environ, populate_request=True, shallow=False):
        self.environ = environ
        if populate_request and not shallow:
            self.environ['werkzeug.request'] = self
        self.shallow = shallow

这里写图片描述

回过头来我们继续看下__call__方法中的wsgi_app方法

       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)

我们就分析完毕ctx = self.request_context(environ),然后我们看下面一行代码

ctx.push()
    def push(self):
        "省略一些代码"

        self.session = self.app.open_session(self.request)
        if self.session is None:
            self.session = self.app.make_null_session()

在上述代码self.app.open_session(self.request),跟踪代码,进入到Flask类如下方法

    def open_session(self, request):
        return self.session_interface.open_session(self, request)

我们e是session_interface = SecureCookieSessionInterface()
然后session_interface.open_session(self, request),我们先看session_interface类做了什么操作?

class SecureCookieSessionInterface(SessionInterface):
    salt = 'cookie-session'

    digest_method = staticmethod(hashlib.sha1)

    key_derivation = 'hmac'

    serializer = session_json_serializer
    session_class = SecureCookieSession

    def get_signing_serializer(self, app):
        if not app.secret_key:
            return None
        signer_kwargs = dict(
            key_derivation=self.key_derivation,
            digest_method=self.digest_method
        )
        return URLSafeTimedSerializer(app.secret_key, salt=self.salt,
                                      serializer=self.serializer,
                                      signer_kwargs=signer_kwargs)

    def open_session(self, app, request):
        s = self.get_signing_serializer(app)
        if s is None:
            return None
        val = request.cookies.get(app.session_cookie_name)
        if not val:
            return self.session_class()
        max_age = total_seconds(app.permanent_session_lifetime)
        try:
            data = s.loads(val, max_age=max_age)
            return self.session_class(data)
        except BadSignature:
            return self.session_class()

    def save_session(self, app, session, response):
        domain = self.get_cookie_domain(app)
        path = self.get_cookie_path(app)

        if not session:
            if session.modified:
                response.delete_cookie(app.session_cookie_name,
                                       domain=domain, path=path)
            return

        if not self.should_set_cookie(app, session):
            return

        httponly = self.get_cookie_httponly(app)
        secure = self.get_cookie_secure(app)
        expires = self.get_expiration_time(app, session)
        val = self.get_signing_serializer(app).dumps(dict(session))
        response.set_cookie(app.session_cookie_name, val,
                            expires=expires, httponly=httponly,
                            domain=domain, path=path, secure=secure)

然后看下该类的open_session方法

    def open_session(self, app, request):
        s = self.get_signing_serializer(app)
        if s is None:
            return None
        val = request.cookies.get(app.session_cookie_name)
        if not val:
            return self.session_class()
        max_age = total_seconds(app.permanent_session_lifetime)
        try:
            data = s.loads(val, max_age=max_age)
            return self.session_class(data)
        except BadSignature:
            return self.session_class()

open_session方法中,通过 s = self.get_signing_serializer(app)获取一个<itsdangerous.URLSafeTimedSerializer object at 0x05DC9490>对象用来进行序列化操作的
然后继续执行val = request.cookies.get(app.session_cookie_name)代码,用来获取如下的键值对(我本机测试)
session=eyJrMSI6MTIzfQ.DcHu9w.Ko6qHGq3iopv0dWG5YDGixnKtU4
然后通过data = s.loads(val, max_age=max_age)获取我们设置的session值(我自己本机测试格式如下)

{'k1': 123} 

然后通过self.session_class(data)封装成如下的对象

 <SecureCookieSession {'k1': 123}>

如果请求没有带来session,会返回self.session_class()对象,

session_class = SecureCookieSession

我们继续回过头来看下wsgi_app方法,以上的简单梳理就是执行了如下的2行代码

ctx = self.request_context(environ)
ctx.push()

然后看wsgi_app方法try内部代码段

        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)

response = self.full_dispatch_request()

    def full_dispatch_request(self):
        self.try_trigger_before_first_request_functions()
        try:
            request_started.send(self)
            rv = self.preprocess_request()

            if rv is None:
                rv = self.dispatch_request()
        except Exception as e:
            rv = self.handle_user_exception(e)
        return self.finalize_request(rv)

Flask将调用full_dispatch_request函数进行请求的分发,之所以不用给参数,是因为我们可以通过request对象获得这次请求的信息。full_dispatch_request将根据请求的url找到对应的蓝本里面的视图函数,并生成一个response对象
是如何生成response呢?
通过rv = self.dispatch_request()获取要进行转发的当前的内容,然后通过self.finalize_request(rv)生成最终对象,并且返回

    def finalize_request(self, rv, from_error_handler=False):
        response = self.make_response(rv)
        try:
            response = self.process_response(response)
            request_finished.send(self, response=response)
        except Exception:
            if not from_error_handler:
                raise
            self.logger.exception('Request finalizing failed with an '
                                  'error while handling an error')
        return response

finalize_request方法中,通过response = self.make_response(rv)构造response对象

 response_class = Response
class Response(BaseResponse, ETagResponseMixin, ResponseStreamMixin,
               CommonResponseDescriptorsMixin,
               WWWAuthenticateMixin):

最后在wsgi_app中return response(environ, start_response)返回即可
然后会去执行werkzeug下的__call__方法

    def __call__(self, environ, start_response):

        app_iter, status, headers = self.get_wsgi_response(environ)
        start_response(status, headers)
        return app_iter
def start_response(status, response_headers, exc_info=None):
            if exc_info:
                try:
                    if headers_sent:
                        reraise(*exc_info)
                finally:
                    exc_info = None
            elif headers_set:
                raise AssertionError('Headers already set')
            headers_set[:] = [status, response_headers]
            return write

start_response返回write方法,然后跟踪该方法,最终写回浏览器
以上就是简单的流程

没有更多推荐了,返回首页