本文通过解析Flask0.1的源码,讲解一下Flask框架的主要工作流程。为了方便理解,后面涉及到的部分源码只保留核心部分,要看完整版可以点这里:Flask0.1的源码。
启动应用
我们先看一下 Flask 的简单使用:
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello, World!'
if __name__ == "__main__":
app.run()
可以看出应用启动的代码是app.run() ,这个方法的代码如下:
def run(self, host='localhost', port=5000, **options):
from werkzeug import run_simple
return run_simple(host, port, self, **options)
run() 方法只是调用了werkzeug的run_simple() 方法,把self 作为参数传进去了,这里的self 就是上面创建的Flask对象。
run_simple() 方法会启动一个WSGI服务器。关于WSGI的概念,上一篇 Flask源码解析(一):WSGI 中已经写过了,这里就不再赘述了。要说一下的是,WSGI服务器的主要工作包括:监听指定的端口,接收到HTTP请求,根据WSGI协议解析请求数据并调用app对象。 在werkzeug.serving:WSGIRequestHandler 的run_wsgi() 方法中可以看到调用app的逻辑如下:
def execute(app):
application_iter = app(environ, start_response)
try:
for data in application_iter:
write(data)
if not headers_sent:
write('')
finally:
if hasattr(application_iter, 'close'):
application_iter.close()
application_iter = None
可以看到,这里把app 对象作为方法调用,其实就相当于调用了Flask类的__call__ 方法:
def __call__(self, environ, start_response):
return self.wsgi_app(environ, start_response)
请求处理
在wsgi_app() 方法中会进行一次HTTP请求的核心处理流程:
def wsgi_app(self, environ, start_response):
with self.request_context(environ):
rv = self.preprocess_request()
if rv is None:
rv = self.dispatch_request()
response = self.make_response(rv)
response = self.process_response(response)
return response(environ, start_response)
下面就分别介绍wsgi_app() 方法中调用的各个方法。
创建上下文
with self.request_context(environ) 的作用是创建上下文,关于上下文的内容不在这里展开,之后会专门写一篇文章来讲。
请求预处理
当使用before_request 装饰器时,会将装饰的函数加入到before_request_funcs 列表中来:
def before_request(self, f):
"""Registers a function to run before each request."""
self.before_request_funcs.append(f)
return f
preprocess_request() 会遍历执行before_request 装饰的函数:
def preprocess_request(self):
for func in self.before_request_funcs:
rv = func()
if rv is not None:
return rv
注意:只要有一个预处理函数返回非None ,则不再执行后面的函数,直接返回结果,也不再执行dispatch_request() 进行请求调度。
请求调度
dispatch_request() 会找到对应的处理函数,调用并返回处理函数的结果:
def dispatch_request(self):
endpoint, values = self.match_request()
return self.view_functions[endpoint](**values)
构造Response对象
make_response() 将处理函数的结果转换为Response类型的对象
def make_response(self, rv):
if isinstance(rv, self.response_class):
return rv
if isinstance(rv, basestring):
return self.response_class(rv)
if isinstance(rv, tuple):
return self.response_class(*rv)
return self.response_class.force_type(rv, request.environ)
请求后处理
当使用after_request 装饰器时,会将装饰的函数加入到after_request_funcs 列表中来:
def after_request(self, f):
"""Register a function to be run after each request."""
self.after_request_funcs.append(f)
return f
process_response() 方法先将session 的信息保存到response 中,然后遍历执行after_request 装饰的函数
def process_response(self, response):
session = _request_ctx_stack.top.session
if session is not None:
self.save_session(session, response)
for handler in self.after_request_funcs:
response = handler(response)
return response
路由
路由就是根据请求的 URL 找到对应处理函数的过程。上面讲到dispatch_request() 会找到对应的处理函数,那它是如何找到的呢。
我们先来了解一下路由规则的注册,定义处理函数的时候我们会用app.route() 装饰器来注册路由规则:
@app.route('/')
def hello_world():
return 'Hello World!'
我们来看看app.route() 方法中做了什么:
def route(self, rule, **options):
def decorator(f):
self.add_url_rule(rule, f.__name__, **options)
self.view_functions[f.__name__] = f
return f
return decorator
def add_url_rule(self, rule, endpoint, **options):
options['endpoint'] = endpoint
options.setdefault('methods', ('GET',))
self.url_map.add(Rule(rule, **options))
可以这样简单理解,route 装饰器会在url_map 中保存把url规则和函数名的映射关系,在view_functions 中保存函数名和函数对象的映射关系。
然后我们回过头来看一下dispatch_request() 方法,继续分析路由的过程:
def dispatch_request(self):
endpoint, values = self.match_request()
return self.view_functions[endpoint](**values)
def match_request(self):
rv = _request_ctx_stack.top.url_adapter.match()
request.endpoint, request.view_args = rv
return rv
match_request() 方法会调用url_adapter.match() 获取到函数名endpoint 和参数values ,然后根据endpoint 从view_functions 获取处理函数并调用。 这个url_adapter 对象是在请求上下文中初始化的,代码如下:
class _RequestContext(object):
def __init__(self, app, environ):
self.app = app
self.url_adapter = app.url_map.bind_to_environ(environ)
self.request = app.request_class(environ)
self.session = app.open_session(self.request)
self.g = _RequestGlobals()
self.flashes = None
结合以上两段代码,可以看出获取endpoint和参数是由url_map.bind_to_environ(environ).match() 来实现的。 url_map 中存储的是url与endpoint之间的映射关系,而environ 中保存着请求信息,包括请求的url。可以这样理解,存储信息的对象url_map 根据environ 中的url进行匹配,就能获取到请求对应的endpoint和参数。
异常处理
dispatch_request() 的完整代码其实还包括异常处理(上面的是缩略版):
def dispatch_request(self):
try:
endpoint, values = self.match_request()
return self.view_functions[endpoint](**values)
except HTTPException, e:
handler = self.error_handlers.get(e.code)
if handler is None:
return e
return handler(e)
except Exception, e:
handler = self.error_handlers.get(500)
if self.debug or handler is None:
raise
return handler(e)
当使用errorhandler 装饰器时,会将装饰的函数加入到error_handlers 字典中来,这里的code参数是http状态码
def errorhandler(self, code):
def decorator(f):
self.error_handlers[code] = f
return f
return decorator
在dispatch_request() 方法中,当match_request() 方法或处理函数抛出异常时,会根据不同情况进行处理:
- 当抛出的异常类型是HTTPException
- 如果根据http状态码能在
error_handlers 里找到对应的处理函数,则调用处理函数并返回结果 - 没有找到的话则直接返回这个异常,之后会由
make_response() 方法处理成http状态码对应的报错页面 - 当抛出的异常类型不是HTTPException
- 如果是debug模式或找不到处理函数,则把异常直接抛出给WerkZeug框架
- 如果是debug模式,WerkZeug会打印报错堆栈信息,并返回报错堆栈页面
- 如果是非debug模式,WerkZeug会打印报错堆栈信息,并返回状态码500对应的Internal Server Error页面
- 如果不是debug模式且找到了处理函数,则调用处理函数并返回结果
|