Flask 源码阅读(1) : request的处理流程

Flask 源码阅读(1) : request的处理流程

August 2, 2020
Flask 源码阅读
Python, Flask

前言 #

本系列试图从flask 1.0版开始阅读一个完整的项目,不同于常见的追踪启动后行为来阅读源码的方式,这一个系列的文章打算从写一个符合wsgi方式的web框架开始,然后逐步完善添加功能的方式来写阅读笔记。

wsgi规范 #

def app(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/plain')])
    yield "Hello world!\n"

一个简单的 wsgi 规范的 app 会被构建为以上的形式。其中app作为即将扩展的部分,需要被实现为一个 Callable 对象,它接受 environ 和 start_response 两个参数,并返回一个字节序列。

flask 的app实现 #

以上提到 app 需要被实现为一个 callable 对象,那么,作为主体的 Flask 类则要定义 __call__() 函数。

def __call__(self, environ, start_response):
        return self.wsgi_app(environ, start_response)

可以看到__call__ 函数其实只是返回了wsgi_app(environ, start_response), 真正的app主体在 wsgi_app上,__call__() 只是它的一个包装。通过这种方式可以分离 app 方法,便于对 app 进行修改。

观察wsgi_app() 方法

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

方法主体十分简单,首先根据 environ 构造 context , 然后通过 full_dispatch_request() 解析请求返回函数 responseresponse(environ, start_response) 函数返回真正需要获得的字节序列。

request_context() 函数 #

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

返回了 RequestContext 类, 暂且跳过。

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)

如上所说,full_dispatch_request() 函数作为主体主要执行了以下几件事

try_trigger_before_first_request_functions() : 这个函数并不复杂,简单来说是在第一个 request 之前初始化并依次执行self.before_first_request_funcs中的函数。

request_started.send(self) : 这一部分request_started作为_FakeSingnal对象用来发送 request start的信号

preprocess_request() : 预处理请求,先由上下文栈弹出蓝图,调用url_value_preprocessors.get(none, ())来获取针对所有 url 值需要的函数,如果有针对所有 url 都起作用的视图函数,则在此调用。接下来调用每一个使用before_request装饰的可作用于所有 request 的函数。如果其中某一个函数返回一个值,这个值将会作为视图返回值 处理并停止进一步的请求处理。

dispatch_request() :本函数进行url匹配,返回视图函数返回值。由栈顶弹出 request,然后rule = req.url_rul,如果 rule 含有 provide_automatic_options,自动调用self.make_default_options_response()函数并返回。否则返回self.view_functions[rule.endpoint] (**req.view_args),可以看出这里仅通过endpoint来匹配 view function 字典。

finalize_request() : 这个函数将先调用make_response(rv)视图返回值转变为真正的 response (这一部分之后再谈)。接下来是函数主要部分,process_response(response)函数获取上下文,获得蓝图和上下文中 request 之后需要调用的函数,接下来构造执行链 ctx.after_request_functions->(逆置)bp.after_request_functions->适用于所有的 request 的 (逆置) after_request_functions。按照执行链依次执行,最终储存session,结束request。

小结 #

本文作为 flask 源码阅读的第一篇,简略的浏览了一遍 app 中关于 request 的处理流程,设计方式基本十分直观,主体分为三部分,在 request 之前,request 处理, request 之后构建了相关的执行链,并进行调用。 接下来一篇将试图观察视图函数的注册以及 route 部分的实现。