按照官方介绍,Routes 是 Rails routes 的 Python 版,用于将不同的 URL 映射到对应的应用上,这对于开发 Restful URL 非常便利:
Routes is a Python re-implementation of the Rails routes system for mapping URLs to application actions, and conversely to generate URLs. Routes makes it easy to create pretty and concise URLs that are RESTful with little effort.
注:关于 REST API,可参考以下优秀博文:
- REST API 作者的博文论文:Architectural Styles and the Design of Network-based Software Architectures (中文版)
- Best Practices for Designing a Pragmatic RESTful API (中文版)
基本使用
对于 Routes,我们平常使用得比较多的就是其 Mapper 工具了。可以看看官方提供的样例:
1 | # Setup a mapper |
Routes in OpenStack
这里我们仿照 OpenStack Keystone 写了下边一个样例:
1 | import webob.dec |
这里有几点需要注意的:
- 因为服务端跟应用之间交互的标准是 WSGI,所以 Router 本身必须是一个 WSGI 应用(__call__(self.req))。
Controller 路由
—— Router 对象初始化时会调用 routes 中间件(RoutesMiddleware
),然后调用self._dispatch
应用,将路由转发给相应的应用(UserController 和 TenantController)。Router 实际做的只是路由到具体到 Controller 而已,而具体要路由到的最终方法(action)是不知道的
,且看下句:具体方法路由
—— UserController 或 TenantController 被调用时(__call__(self.req)),对传入参数进行处理,并决定要调用的方法(method = getattr(self, action)),最后调用该方法并将结果标准化后返回。
对于 routes 中间件 RoutesMiddleware
和 self._dispatch
,这里详细说一下:
- self._dispatch 本身是一个 WSGI 应用,它和 self._mapper 一起作为参数初始化 routes.middleware.RoutesMiddleware 这个类。
- RoutesMiddleware 也是一个 WSGI 应用(注意跟 self._dispatch 不是一个应用),当调用该应用时(__call__),会进行路由的匹配(注意这个参数
wsgiorg.routing_args
):1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42# routes/middleware.py
class RoutesMiddleware(object):
......
def __call__(self, environ, start_response):
"""Resolves the URL in PATH_INFO, and uses wsgi.routing_args
to pass on URL resolver results."""
......
# Run the actual route matching
# -- Assignment of environ to config triggers route matching
if self.singleton:
config = request_config()
config.mapper = self.mapper
config.environ = environ
match = config.mapper_dict
route = config.route
else:
results = self.mapper.routematch(environ=environ)
if results:
match, route = results[0], results[1]
else:
match = route = None
......
url = URLGenerator(self.mapper, environ)
environ['wsgiorg.routing_args'] = ((url), match)
environ['routes.route'] = route
environ['routes.url'] = url
......
response = self.app(environ, start_response) # 调用 self._dispatch 这个 WSGI 应用
# Wrapped in try as in rare cases the attribute will be gone already
try:
del self.mapper.environ
except AttributeError:
pass
return response - RoutesMiddleware 最后会调用 self._dispatch 这个 WSGI 应用,然后再由其调用对象的 Controller(WSGI 应用)。
Routes Resource
在实际中,对某个资源一般会有一些常见操作,如 CRUD,于是乎,用上一小节的方法,我们可能就要自己添加多个路由映射:
1 | class Router(object): |
但我们要操作的资源较少时,这种做法问题不大,但当资源种类特别多的时候,这种方法就有点笨拙了,尤其是像 OpenStack Nova 模块涉及的资源太多,要是单纯为每个资源都添加个 CRUD,这个路由映射表就够大的了;另外,这种做法也不符合复用的思想。所以,我们期望,对资源的一些常见操作,我们希望 routes 包能够帮我们封装好;对一些非常见操作提供支持。
要做到这个,就要用到 routes.resource
这个工具了。
关于 routes.resource 详细介绍,可参考其官方文档。接下来我们就直接上样例吧:
1 | ...... |
我们可以利用发包工具(如 POSTMAN)向服务器发送请求,下边是服务器截获到的请求:
1 | 127.0.0.1 - - [21/May/2017 15:39:37] "GET /users HTTP/1.1" 200 23 |
不同的路由就会进入不同的处理函数。
Routes and PasteDeploy
只有将 Routes 和 PasteDeploy 相结合起来,才能算完满!那就开始吧!
这两者一旦放在一起就容易混淆,不过只要清楚它们的作用,区分开来还是比较简单的。简单点讲,PasteDeploy “主外”,Routes “主内”,也即,PasteDeploy 关注的是中间件加应用的流水线,流水线终点是 Router,是大的方面;Routes 关注的是最终 URL 的分发,关注的是分发到具体应用(Controller),是小的方面。
下边我们来直接看样例吧:
配置文件:
1 | # resource.ini |
源码:
1 | # resource.py |
先这样吧,有需要再继续改进!