本文主要梳理了flask源码中route的设计思路。
首先,从WSGI协议的角度介绍flask route的作用;
其次,详细讲解如何借助werkzeug库的Map、Rule实现route;
最后,梳理了一次完整的http请求中route的完整流程。
本文参考的是flask 0.5版本的代码。
flask 0.1版本的代码非常短,只有600多行,但是这个版本缺少blueprint机制。
因此,我参考的是0.5版本。
直接使用flask官方文档中的例子
| 1234567891011121314 | from flask import Flaskapp = Flask(__name__) @app.route(\’/\’)def hello_world(): return \’Hello World!\’ @app.route(\’/post/\’)def show_post(post_id): # show the post with the given id, the id is an integer return \’Post %d\’ % post_id if __name__ == \’__main__\’: app.run() |
此例中,使用app.route装饰器,完成了以下两个url与处理函数的route:
| 1234 | { \’/\’: hello_world, \’/post/\’ : show_post} |
这样做的效果为:
当http请求的url为’/’时,flask会调用hello_world函数;
当http请求的url为’/post/’(例如/post/32)时,flask会调用show_post函数;
从上面的示例中其实可以明白:flask route的作用就是建立url与处理函数的映射。
WSGI协议将处理请求的组件按照功能及调用关系分成了三种:server, middleware, application。
其中,server可以调用middleware和application,middleware可以调用application。
符合WSGI的框架对于一次http请求的完整处理过程为:
server读取解析请求,生成environ和start_response,然后调用middleware;
middleware完成自己的处理部分后,可以继续调用下一个middleware或application,形成一个完整的请求链;
application位于请求链的最后一级,其作用就是生成最终的响应。
| 12 | http服务器(比如,nginx)—> WSGI server(比如gunicorn,SimpleHttpServer)—>middleware—> middleware—> ... —>application |
如果接触过Java Web 开发的人可能会立刻发现,这与servlet中的middleware机制是完全一致的。
特别重要的:
在上一小节的示例中
app = Flask(__name__)创建了一个middleware,
而这个middleware的核心作用是进行请求转发(request dispatch)。
上面这句话非常重要,请在心里重复一百遍。
上面这句话非常重要,请在心里重复一百遍。
上面这句话非常重要,请在心里重复一百遍。
进行请求转发的前提就是能够建立url与处理函数之间的映射关系,即route功能。
因此,在flask中,route是Flask类的一个装饰器。
通过上一小节,我们知道以下两点:
flask route 是url与处理函数的映射关系;Flask这个middleware负责完成对url对应的处理函数的调用;那么,如果是我们自己来实现route,思路也很简单:
Flask,这个类是一个middleware,并且有一个字典型的成员变量url_map;url_map = {url : function}function,然后调用function;flask的实现思路也是这样的。
| 123456789101112131415161718 | class Flask(object): def __init__(self): self.url_map = {} # 此处定义保存url与处理函数的映射关系 def __call__(self, environ, start_response): # 根据WSGI协议,middleware必须是可调用对象 self.dispatch_request() # Flask的核心功能 request dispatch return application(environ, start_response) #最后调用下一级的application def route(self, rule): # Flask使用装饰器来完成url与处理函数的映射关系建立 def decorator(f): # 简单,侵入小,优雅 self.url_map[rule] = f return f return decorator def dispath_request(self): url = get_url_from_environ() #解析environ获得url return self.url_map[url]() #从url_map中找到对应的处理函数,并调用 |
至此, 一个简单的Flaskmiddleware的骨架就完成了。
上面的Flask类主要功能包括:
当然,在实际中,不可能这么简单,但是基本思路是一致的。
需要指出,上面实现的最简单的Flask类还是有很多问题的。
比如,HTTP请求中相同的url,不同的请求方法,比如GET,POST如果对应不同的处理函数,该如何处理?
flask使用了werkzeug库中的Map和Rule来管理url与处理函数映射关系。
首先需要简单了解一下Map和Rule的作用:
在werkzeug中,Rule的主要作用是保存了一组url,endpoint,methods关系:
每个(url, endpoint, methods)都有一个对应的Rule对象:
其实现如下:
| 12345 | class Rule(object): def __init__(self, url, endpoint, methods): self.rule = url self.endpoint = endpoint self.methods = methods |
这里需要解释一下endpoint:
前面说过:url与其处理函数可以使用一个字典来实现:{url: function}
flask在实现的时候,在中间加了一个中介endpoint,于是,url与处理函数的映射yon-h\”> methods
这里需要解释一下endpoint:
前面说过:url与其处理函数可以使用一个字典来实现:{url: function}
flask在实现的时候,在中间加了一个中介endpoint,于是,url与处理函数的映射 Ό整流程。
本文参考的是flask 0.5版本的代码。
flask 0.1版本的代码非常短,只有600多行,但是这个版本缺少blueprint机制。
因此,我参考的是0.5版本。
直接使用flask官方文档中的例子
| 1234567891011121314 | from flask import Flaskapp = Flask(__name__) @app.route(\’/\’)def hello_world(): return \’Hello World!\’ @app.route(\’/post/\’)def show_post(post_id): # show the post with the given id, the id is an integer return \’Post %d\’ % post_id if __name__ == \’__main__\’: app.run() |
此例中,使用app.route装饰器,完成了以下两个url与处理函数的route:
| 1234 | { \’/\’: hello_world, \’/post/\’ : show_post} |
这样做的效果为:
当http请求的url为’/’时,flask会调用hello_world函数;
当http请求的url为’/post/’(例如/post/32)时,flask会调用show_post函数;
从上面的示例中其实可以明白:flask route的作用就是建立url与处理函数的映射。
WSGI协议将处理请求的组件按照功能及调用关系分成了三种:server, middleware, application。
其中,server可以调用middleware和application,middleware可以调用application。
符合WSGI的框架对于一次http请求的完整处理过程为:
server读取解析请求,生成environ和start_response,然后调用middleware;
middleware完成自己的处理部分后,可以继续调用下一个middleware或application,形成一个完整的请求链;
application位于请求链的最后一级,其作用就是生成最终的响应。
| 12 | http服务器(比如,nginx)—> WSGI server(比如gunicorn,SimpleHttpServer)—>middleware—> middleware—> ... —>application |
如果接触过Java Web 开发的人可能会立刻发现,这与servlet中的middleware机制是完全一致的。
特别重要的:
在上一小节的示例中
app = Flask(__name__)创建了一个middleware,
而这个middleware的核心作用是进行请求转发(request dispatch)。
上面这句话非常重要,请在心里重复一百遍。
上面这句话非常重要,请在心里重复一百遍。
上面这句话非常重要,请在心里重复一百遍。
进行请求转发的前提就是能够建立url与处理函数之间的映射关系,即route功能。
因此,在flask中,route是Flask类的一个装饰器。
通过上一小节,我们知道以下两点:
flask route 是url与处理函数的映射关系;Flask这个middleware负责完成对url对应的处理函数的调用;那么,如果是我们自己来实现route,思路也很简单:
Flask,这个类是一个middleware,并且有一个字典型的成员变量url_map;url_map = {url : function}function,然后调用function;flask的实现思路也是这样的。
| 123456789101112131415161718 | class Flask(object): def __init__(self): self.url_map = {} # 此处定义保存url与处理函数的映射关系 def __call__(self, environ, start_response): # 根据WSGI协议,middleware必须是可调用对象 self.dispatch_request() # Flask的核心功能 request dispatch return application(environ, start_response) #最后调用下一级的application def route(self, rule): # Flask使用装饰器来完成url与处理函数的映射关系建立 def decorator(f): # 简单,侵入小,优雅 self.url_map[rule] = f return f return decorator def dispath_request(self): url = get_url_from_environ() #解析environ获得url return self.url_map[url]() #从url_map中找到对应的处理函数,并调用 |
至此, 一个简单的Flaskmiddleware的骨架就完成了。
上面的Flask类主要功能包括:
当然,在实际中,不可能这么简单,但是基本思路是一致的。
需要指出,上面实现的最简单的Flask类还是有很多问题的。
比如,HTTP请求中相同的url,不同的请求方法,比如GET,POST如果对应不同的处理函数,该如何处理?
flask使用了werkzeug库中的Map和Rule来管理url与处理函数映射关系。
首先需要简单了解一下Map和Rule的作用:
在werkzeug中,Rule的主要作用是保存了一组url,endpoint,methods关系:
每个(url, endpoint, methods)都有一个对应的Rule对象:
其实现如下:
| 12345 | class Rule(object): def __init__(self, url, endpoint, methods): self.rule = url self.endpoint = endpoint |