Flask 源码分析-基本工作流程
Posted 大路上的小强
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Flask 源码分析-基本工作流程相关的知识,希望对你有一定的参考价值。
本文主要分析了 Flask 的基本工作流程,由于处理 URL 请求与路由规则强相关,因此先介绍了Flask是怎样建立路由规则,然后详细分析了 Flask 的基本工作流程。
建立路由规则
客户端把请求发给 Web 服务器,Web 服务器再把请求发送给程序实例,程序实例需要知道每个 URL 请求运行哪些代码,所以保存了一个URL到处理函数的映射关系,处理 URL 和函数之间关系的程序称为路由。
Flask 建立路由规则的方法一般通过 @route
装饰器对视图函数进行装饰,例如:
app = Flask(__name__)@app.route('/')def index(): return 'Hello world!'
该方法也可以写成
app = Flask(__name__)def index(): return 'Hello world!'app.add_url_rule('/', 'hello', hello)
建立路由规则时,Flask 究竟做了什么,我们来看看 route
函数:
def route(self, rule, **options): def decorator(f): endpoint = options.pop('endpoint', None) self.add_url_rule(rule, endpoint, f, **options) return f return decorator
route
函数是一个装饰器,获取参数 options
中的 endpoint
后,调用 add_url_rule
添加路由规则,返回被装饰的函数,这也验证了上面两种方法等价的说法。add_url_rule
是怎样添加路由规则的?
def add_url_rule(self, rule, endpoint=None, view_func=None, **options): ... rule = self.url_rule_class(rule, methods=methods, **options) rule.provide_automatic_options = provide_automatic_options self.url_map.add(rule) if view_func is not None: old_func = self.view_functions.get(endpoint) if old_func is not None and old_func != view_func: raise AssertionError('View function mapping is overwriting an ' 'existing endpoint function: %s' % endpoint) self.view_functions[endpoint] = view_func
这段代码省略了获取 endpoint
和 methods
的部分,它主要做了两件事,一是根据 URL、methods
和endpoint
创建 Rule
对象,将其添加到变量 url_map
中,Rule
对象是 werkzeug.routing:Rule
类的对象,url_map
是 werkzeug.routeing:Map
类的对象,这两个类的具体作用会在后面介绍。add_url_rule
函数做的第二件事是将视图函数添加到字典 view_functions
中,对应的键为endpoint
,这样路由规则就创建完成。
根据上面的处理过程,我们可以猜想一下,当客户端发来 URL 请求,Flask 是怎样处理的:先根据请求中的 url
,method
与 url_map
中的 rule
进行匹配,若能匹配上,就能获取 rule
对应的endpoint
,然后查找字典 view_funciton
,得到前面添加的视图函数,视图函数对请求进行处理,返回结果。
工作流程
当服务器收到 http 请求,调用 Flask 应用时,实际上是调用的 Flask 的 __call__
方法,从 __call__
方法的源码可以看出,最终是调用了 wsgi_app
方法并传入 environ
和 start_response
,environ
为请求头的所有信息,start_response
则是 Flask应用 处理完之后需要调用的函数,参数是状态码、响应头部还有错误信息。。
def __call__(self, environ, start_response): """Shortcut for :attr:`wsgi_app`.""" return self.wsgi_app(environ, start_response)
wsgi_app
的定义如下,基本上包含了整个流程的功能
def wsgi_app(self, environ, start_response): """ :param environ: a WSGI environment :param start_response: a callable accepting a status code, a list of headers and an optional exception context to start the 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) return response(environ, start_response) finally: if self.should_ignore_error(error): error = None ctx.auto_pop(error)
1. 创建request上下文
ctx = self.request_context(environ)
语句生成了一个 request
上下文,具体做了什么,我们来看看 RequestContext
的初始化函数,
class RequestContext(object): """The request context contains all request relevant information. It is created at the beginning of the request and pushed to the `_request_ctx_stack` and removed at the end of it. It will create the URL adapter and request object for the WSGI environment provided. """ 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 self.match_request() ... def match_request(self): """Can be overridden by a subclass to hook into the matching of the request. """ try: url_rule, self.request.view_args = \ self.url_adapter.match(return_rule=True) self.request.url_rule = url_rule except HTTPException as e: self.request.routing_exception = e
在初始化的时候,首先创建了 request
对象,会调用 app.create_url_adapter
方法,把 app
的url_map
绑定到 request
的 WSGI environ
变量上,创建 MapAdapter
对象,最后会调用match_request
方法,返回匹配到的 url_rule
以及 url 中的参数。 request
上下文的具体实现以及栈结构实现较复杂,在后续文章中会专门分析。
2. 分发请求
这里省略了 full_dispatch_reques
函数中请求预处理,错误处理的流程,重点分析dispatch_reques
函数。request
上下文栈结构弹出当前的 request
对象,取出先前匹配到的url_rule
,根据 rule.endpoint
取出字典 view_functions
中的视图函数,完成对本次 URL 请求的处理。
def dispatch_request(self): """Does the request dispatching. Matches the URL and returns the return value of the view or error handler. This does not have to be a response object. In order to convert the return value to a proper response object, call :func:`make_response`. """ req = _request_ctx_stack.top.request if req.routing_exception is not None: self.raise_routing_exception(req) rule = req.url_rule if getattr(rule, 'provide_automatic_options', False) \ and req.method == 'OPTIONS': return self.make_default_options_response() return self.view_functions[rule.endpoint](**req.view_args)
3. 生成响应
分发请求完成后,已经取得了返回值,再看下一步是如何做,回到 full_dispatch_request
,它调用了 finalize_request
。该函数通过 make_response
函数,将刚才取得的 rv
生成响应,重新赋值response
,self.process_response(response)
语句主要处理 after_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)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
最后,在wsgi_app
函数中,将从 full_dispatch_request
返回的 response
加上参数environ
,start_response
并返回给服务器。
以上是关于Flask 源码分析-基本工作流程的主要内容,如果未能解决你的问题,请参考以下文章
flask 请求处理流程及其源代码分析sessioncookie生成源码
Android 逆向整体加固脱壳 ( DEX 优化流程分析 | DexPrepare.cpp 中 dvmOptimizeDexFile() 方法分析 | /bin/dexopt 源码分析 )(代码片段
Android 逆向ART 脱壳 ( DexClassLoader 脱壳 | DexClassLoader 构造函数 | 参考 Dalvik 的 DexClassLoader 类加载流程 )(代码片段
Android 逆向ART 脱壳 ( DexClassLoader 脱壳 | DexClassLoader 构造函数 | 参考 Dalvik 的 DexClassLoader 类加载流程 )(代码片段