Routes
Routes是使用Python重新实现了Rails Routes系统的库。Routes用来将URL映射到应用的行为,也可以反过来生成URL。对于RESTful,使用Routes可以创建简洁明了的URL。
对于Web开发了说,设计URL以及URL到代码的映射是很关键的。使用直接映射的方法是最原始和最简单的,例如:/dir/file/function => dir.file.function,就可以将URL映射到目录dir下文件file中的函数function,如果使用类,则URL可以设计
成/file/class/method => file.class.method,即映射到指定类中的方法。使用直接映射的方式,定义和增加映射时,比较繁琐;并且当需要修改URL设计或者对应代码时,工作量比较大。使用Routes可以将URL的设计和其映射的代码分离开来。
可以通过pip或easy_install来安装Routes
参考资料:
Routes Documentation
connect
routes库提供的最重要的类是Mapper,此类负责url映射的建立、保存和匹配。可以使用Mapper的方法connect()来建立url映射。
我们先来介绍一下connect()的参数。
name:路由的名称。可以指定希望的路由名,也可以使用None以建立无名路由。
routepath:路由的路径。
controller:控制其的名称,字符串形式
action:行动的名称,字符串形式
conditions:限制,字典形式
requirements:要求,字典形式
下面是一个使用connect()建立路由的例子
from routes import Mapper
mapper = Mapper()
mapper.connect('volume-index',
'/volumes',
controller='volume_controller',
action='index')
mapper.connect('images-show',
'/images',
controller='images_controller',
action='show')
for m in mapper.matchlist:
print('name: \t\t%s' % m.name)
print('routepath: \t%s' % m.routepath)
print('controller: \t%s' % m.defaults['controller'])
print('action: \t\t%s' % m.defaults['action'])
print('-' * 20)
程序输出
name: volume-index routepath: /volumes controller: volume_controller action: index -------------------- name: images-show routepath: /images controller: images_controller action: show --------------------
上面的例子建立了两个路由,路由的信息保存在matchlist中。路由的名称分别为volume-index和images-show。
除了上例中使用connect()的方法外,还有其它的用法,表示如下。
map.connect(None, "/error/{action}/{id}", controller="error")定义了一个route,此route没有名字(即无名route),匹配/error/.../...的url,并能够解析出action和id;
map.connect("home", "/", controller="main", action="index")定义了一个route,此route的名称为home,匹配/的url,并设定controller为main,action为index。
map.connect(None, "/error/{action}/{id}", controller="error", requirements={"id": R"\d+"})只匹配id为数字的url。
map.connect("/download/{platform}/{filename}", requirements={"platform": R"windows|mac"})只匹配platform为"windows"或"mac"的url。
map.connect("/user/list", controller="user", action="list",
conditions=dict(method=["GET"]))只匹配method为GET的url。
参考资料:
Setting up routes
match
可以使用match()函数来获取匹配的结果。match()返回的是一个字典,包含controller和action的名称。下面是一个简单的例子
from routes import Mapper
mapper = Mapper()
mapper.connect('volume-index',
'/volumes',
controller='volume_controller',
action='index')
mapper.connect('images-show',
'/images',
controller='images_controller',
action='show')
for m in mapper.matchlist:
print('name: \t\t%s' % m.name)
print('routepath: \t%s' % m.routepath)
print('controller: \t%s' % m.defaults['controller'])
print('action: \t\t%s' % m.defaults['action'])
print('-' * 20)
urls = ('/abc', '/volumes', '/images')
for url in urls:
result = mapper.match(url)
print('result for %s: %r' % (url, result))
程序输出:
name: volume-index
routepath: /volumes
controller: volume_controller
action: index
--------------------
name: images-show
routepath: /images
controller: images_controller
action: show
--------------------
result for /abc: None
result for /volumes: {'action': u'index', 'controller': u'volume_controller'}
result for /images: {'action': u'show', 'controller': u'images_controller'}
在上面的例子中,先创建类Mapper的实例map,通过方法conect()创建url到应用/行为的映射,通过方法match可以进行匹配,并将结果返回。如果匹配失败,会返回None。对于/abc来说,在所有已建立的路由中都没有找到对应的项,所以会返回None,表
示未找到匹配的路由。对于/volumes和/images,都找到了对应的路由,并返回了对应的ontroller和action。
参考资料:
Routes Documentation
resource
对于开发 RESTful 类型的服务,Routes提供了快捷的方法定义对资源 resource 的url映射:
map.resource("volume", "volumes"),上面一行主要等效于下面的代码:
map.connect("volumes", "/volumes",
controller="volumes", action="create",
conditions=dict(method=["POST"]))
map.connect("volumes", "/volumes",
controller="volumes", action="index",
conditions=dict(method=["GET"]))
map.connect("formatted_volumes", "/volumes.{format}",
controller="volumes", action="index",
conditions=dict(method=["GET"]))
map.connect("new_volume", "/volumes/new",
controller="volumes", action="new",
conditions=dict(method=["GET"]))
map.connect("formatted_new_volume", "/volumes/new.{format}",
controller="volumes", action="new",
conditions=dict(method=["GET"]))
map.connect("/volumes/{id}",
controller="volumes", action="update",
conditions=dict(method=["PUT"]))
map.connect("/volumes/{id}",
controller="volumes", action="delete",
conditions=dict(method=["DELETE"]))
map.connect("edit_volume", "/volumes/{id}/edit",
controller="volumes", action="edit",
conditions=dict(method=["GET"]))
map.connect("formatted_edit_volume", "/volumes/{id}.{format}/edit",
controller="volumes", action="edit",
conditions=dict(method=["GET"]))
map.connect("volume", "/volumes/{id}",
controller="volumes", action="show",
conditions=dict(method=["GET"]))
map.connect("formatted_volume", "/volumes/{id}.{format}",
controller="volumes", action="show",
conditions=dict(method=["GET"]))
对于资源resource来说,主要有四个route名称,对应六个动作:
GET => show
new form => GET
new => POST
edit form => GET
edit => PUT
delete => DELETE
可以使用下面的例子,查看到resource()创建的路由
from routes import Mapper
map = Mapper()
map.resource('volume', 'volumes')
for i in range(len(map.matchlist)):
route = map.matchlist[i]
print('[%r] %r' % (i, route.name))
print('\t regpath: ' + route.regpath)
print('\t method: %r' % route.conditions['method'])
程序输出:
[0] None regpath: /volumes.%(format)s method: ['POST'] [1] None regpath: /volumes method: ['POST'] [2] 'formatted_volumes' regpath: /volumes.%(format)s method: ['GET'] [3] 'volumes' regpath: /volumes method: ['GET'] [4] 'formatted_new_volume' regpath: /volumes/new.%(format)s method: ['GET'] [5] 'new_volume' regpath: /volumes/new method: ['GET'] [6] None regpath: /volumes/%(id)s.%(format)s method: ['PUT'] [7] None regpath: /volumes/%(id)s method: ['PUT'] [8] None regpath: /volumes/%(id)s.%(format)s method: ['DELETE'] [9] None regpath: /volumes/%(id)s method: ['DELETE'] [10] 'formatted_edit_volume' regpath: /volumes/%(id)s/edit.%(format)s method: ['GET'] [11] 'edit_volume' regpath: /volumes/%(id)s/edit method: ['GET'] [12] 'formatted_volume' regpath: /volumes/%(id)s.%(format)s method: ['GET'] [13] 'volume' regpath: /volumes/%(id)s method: ['GET']
参考资料:
RESTful services
RoutesMiddleware
对于一个应用来说,通常会创建一个Mapper实例,然后调用match()方法;而对于一个WSGI框架来说,会使用类似下面的代码来使用RoutesMiddleware:
from routes.middleware import RoutesMiddleware wsgi_app = RoutesMiddleware(app, map)
middleware会匹配请求的URl,并设置以下WSGI变量:
environ['wsgiorg.routing_args'] = ((url, match)) environ['routes.route'] = route environ['routes.url'] = url
其中,url是URLGenerator的实例,match是一个包含匹配信息的字典,route是匹配的路由。
下面,使用middleware创建一个WSGI的Web框架,此框架提供了路由建立和路由匹配的功能,应用层只需专注于业务即可。
整个WSGI的Web框架在文件wsgi.py中实现。代码如下:
import json import routes import routes.middleware import webob import webob.dec import webob.exc class APIMapper(routes.Mapper): pass class Router(object): def __init__(self, mapper): mapper.redirect("", "/") self.map = mapper self._router = routes.middleware.RoutesMiddleware(self._dispatch, self.map) @classmethod def factory(cls, global_conf={}, **local_conf): return cls(APIMapper()) @webob.dec.wsgify def __call__(self, req): return self._router @staticmethod @webob.dec.wsgify def _dispatch(req): match = req.environ['wsgiorg.routing_args'][1] if not match: return webob.exc.HTTPNotFound() app = match['controller'] return app class Request(webob.Request): pass class Resource(object): def __init__(self, controller, deserializer=None, serializer=None): self.controller = controller @webob.dec.wsgify(RequestClass=Request) def __call__(self, request): action_args = self.get_action_args(request.environ) action = action_args.pop('action', None) action_result = self.dispatch(self.controller, action, request, **action_args) try: response = webob.Response( request=request, body=json.dumps(action_result), ) return response except webob.exc.HTTPException as e: return e except Exception as e: print('e: %r' % e) return action_result def dispatch(self, obj, action, *args, **kwargs): try: method = getattr(obj, action) except AttributeError: return {} return method(*args, **kwargs) def get_action_args(self, request_environment): try: args = request_environment['wsgiorg.routing_args'][1].copy() except Exception: return {} try: del args['controller'] except KeyError: pass try: del args['format'] except KeyError: pass return args
业务层的代码位于文件router.py中。主要是调用了框架的接口,实现了URL映射的建立,以及业务功能的具体实现。代码如下:
import Router_test.wsgi as wsgi class VolumeController(object): def __init__(self): pass def index(self, req): return [ {'id': 1, 'name': 'volume-a'}, {'id': 2, 'name': 'volume-b'} ] class APIRouter(wsgi.Router): def __init__(self,mapper): mapper.resource('volume', 'volumes', controller=wsgi.Resource(VolumeController())) super(APIRouter, self).__init__(mapper)
程序的入口位于文件main.py中,主要是通过wsgiref提供的make_server()函数,启动一个WSGI服务。代码如下:
from wsgiref.simple_server import make_server import Router_test.router as router wsgi_app = router.APIRouter.factory() server = make_server('localhost', 8080, wsgi_app) server.serve_forever()
执行python main.py后,在终端使用curl命令访问此WSGI服务,可以发现WSGI服务工作正常
[root@localhost ~]# curl http://localhost:8080/volumes
[{"id": 1, "name": "volume-a"}, {"id": 2, "name": "volume-b"}][root@localhost ~]#
[root@localhost ~]#

浙公网安备 33010602011771号