zoukankan      html  css  js  c++  java
  • flask源码剖析

        这段时间想重新写个自己的博客系统,又正好在看一些框架源码,然后就想要不顺便写个小框架吧,既然想写框架,要不再顺便写个orm吧,再写个小的异步Server吧。。事实证明饭要一口一口吃

        先梳理一下flask工作的整个流程吧。

        首先flask是符合wsgi协议的,那么也就是说,flask要实现一个可以callable的object给web server。

        那么什么是wsgi? 简而言之,wsgi就是把框架和web Server协同工作的一个协议,规范。流程大概是:当有请求来的时候, web Server调用一个callable object(函数,类,实现__call__的实例等等), 传入(environ, start_response), 其中environ是一个字典,包含了请求相关的所有信息,比如请求路径,参数;start_response是一个函数。在这个callable object中会调用start_response,返回http状态码,headers等信息给web server, callable object本身则会返回具体的内容给web server, 返回的object必须是可迭代的。当有callable object本身返回的内容时候, 就会把start_response返回的内容和callable object返回的内容写入缓冲区并且刷新。

      那么flask 给web server的callable object是什么?

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

    可以看到,是一个实现了__call__的实例,web server 调用Flask的实例,传入environ和start_response,那么这个self.wsgi_app是什么东西?

     1     def wsgi_app(self, environ, start_response):
     2         """The actual WSGI application.  This is not implemented in
     3         `__call__` so that middlewares can be applied without losing a
     4         reference to the class.  So instead of doing this::
     5 
     6             app = MyMiddleware(app)
     7 
     8         It's a better idea to do this instead::
     9 
    10             app.wsgi_app = MyMiddleware(app.wsgi_app)
    11 
    12         Then you still have the original application object around and
    13         can continue to call methods on it.
    14 
    15         .. versionchanged:: 0.7
    16            The behavior of the before and after request callbacks was changed
    17            under error conditions and a new callback was added that will
    18            always execute at the end of the request, independent on if an
    19            error ocurred or not.  See :ref:`callbacks-and-errors`.
    20 
    21         :param environ: a WSGI environment
    22         :param start_response: a callable accepting a status code,
    23                                a list of headers and an optional
    24                                exception context to start the response
    25         """
    26         with self.request_context(environ):
    27             try:
    28                 response = self.full_dispatch_request()
    29             except Exception, e:
    30                 response = self.make_response(self.handle_exception(e))
    31             return response(environ, start_response)

    在文档里面第一句已经说明了“The actual WSGI application...”这个才是真正的callable object。那么这个真正的callable object会做什么事情?

    首先,会打开self.request_context(envion),

    class RequestContext(object):
    
        def __init__(self, app, environ):
            self.app = app
            self.request = app.request_class(environ)
            self.url_adapter = app.create_url_adapter(self.request)
            self.g = app.request_globals_class()
            self.flashes = None
            self.session = None
    
    
            self.preserved = False
            self._after_request_functions = []
    
            self.match_request()
    
            blueprint = self.request.blueprint
            if blueprint is not None:
                # better safe than sorry, we don't want to break code that
                # already worked
                bp = app.blueprints.get(blueprint)
                if bp is not None and blueprint_is_module(bp):
                    self.request._is_old_module = True
    
        def match_request(self):
            try:
                url_rule, self.request.view_args = 
                    self.url_adapter.match(return_rule=True)
                self.request.url_rule = url_rule
            except HTTPException, e:
                self.request.routing_exception = e
    
        def push(self):
            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)
    
            _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()
    
        def pop(self, exc=None):
            app_ctx = self._implicit_app_ctx_stack.pop()
    
            clear_request = False
            if not self._implicit_app_ctx_stack:
                self.preserved = False
                if exc is None:
                    exc = sys.exc_info()[1]
                self.app.do_teardown_request(exc)
                clear_request = True
    
            rv = _request_ctx_stack.pop()
            assert rv is self, 'Popped wrong request context.  (%r instead of %r)' 
                % (rv, self)
    
            # get rid of circular dependencies at the end of the request
            # so that we don't require the GC to be active.
            if clear_request:
                rv.request.environ['werkzeug.request'] = None
    
            # Get rid of the app as well if necessary.
            if app_ctx is not None:
                app_ctx.pop(exc)
    
        def __enter__(self):
            self.push()
            return self
    
        def __exit__(self, exc_type, exc_value, tb):
            if self.request.environ.get('flask._preserve_context') or 
               (tb is not None and self.app.preserve_context_on_exception):
                self.preserved = True
            else:
                self.pop(exc_value)

    从__enter__可以看到,首先会把当前请求推入栈,这个是什么栈?有什么用呢?

      简而言之,这个栈就是flask根据每次请求时候,为每个请求所创建的线程/协程的id所创建的一个自己的threadlocal,在这个threadlocal中所实现的一个栈结构,会把不同的请求隔离开来,每次请求时候把当前请求所在的app,request推入栈,就可以在全局可用了。

     把current_app,request推入栈之后,就根据请求的方法,路径等参数分发请求,找到用@app.route('...')注册的函数,生成response。

    但是这里有个问题,就是根据wsgi协议,返回的结果应该是个可迭代的结果,而且这时候start_response还没执行,状态码和其他头部信息还都没有。这个就是make_response的事情了,

        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, e:
                rv = self.handle_user_exception(e)
            response = self.make_response(rv)
            response = self.process_response(response)
            request_finished.send(self, response=response)
            return response

    make_response会把返回结果包装一下。

     def make_response(self, rv):
                if isinstance(rv, basestring):
                    rv = self.response_class(rv, headers=headers, status=status)
                    headers = status = None
                else:
                    rv = self.response_class.force_type(rv, request.environ)
    
            if status is not None:
                if isinstance(status, basestring):
                    rv.status = status
                else:
                    rv.status_code = status
            if headers:
                rv.headers.extend(headers)
    
            return rv

    make_response会利用class Response(BaseResponse)把view function返回的结果包装一下,

    class Response(ResponseBase):
        default_mimetype = 'text/html'
    
    
    class BaseResponse(object):
        automatically_set_content_length = True
    
        def __init__(self, response=None, status=None, headers=None,
             ....
    
           
        @classmethod
        def force_type(cls, response, environ=None):
             ...
    
        @classmethod
        def from_app(cls, app, environ, buffered=False):
            ...
    
        def _get_status_code(self):
            return self._status_code
    
        def __call__(self, environ, start_response):
    
            app_iter, status, headers = self.get_wsgi_response(environ)
            start_response(status, headers)
            return app_iter    

    当执行Response的实例时候,在__call__里面会执行start_response, 同时返回一个可迭代的对象

    这就是flask的执行逻辑

    参考资料:

    PEP 333 -- Python Web Server Gateway Interface v1.0

  • 相关阅读:
    linuxshell中"2>&1"含义
    Java中正数与负数操作>>、>>>的区别
    jsp el表达式
    struct2常用标签
    shell正则表达式
    IPV6学起来很费力?你看看,也就这样简单吧!
    STP、RSTP、MSTP合集思维导图
    HCIE之路--ISIS思维导图
    佛祖保佑,永不宕机! 永无BUG!
    震惊!ARP安全竟然还可以这样配置?
  • 原文地址:https://www.cnblogs.com/fcyworld/p/7856815.html
Copyright © 2011-2022 走看看