pecan的路由机制
基于对象分发的路由机制
pecan使用对象分发的方式将某个HTTP请求(request)映射给某个控制器(controller)。
对象分发机制首先将请求路径分割成一系列的字符串,并根据这些字符串从root控制器开始依次寻找下一级控制器。可以将应用的控制器集合想象成一个对象树,这个树的每一个分支对应了一个URL路径。下面通过代码来介绍pecan的路由机制。
from pecan import expose
class BooksController(object):
@expose()
def index(self):
return "Welcome to book section."
@expose()
def bestsellers(self):
return "We have 5 books in the top 10."
class CatalogController(object):
@expose()
def index(self):
return "Welcome to the catalog."
books = BooksController()
class RootController(object):
@expose()
def index(self):
return "Welcome to store.example.com!"
@expose()
def hours(self):
return "Open 24/7 on the web."
catalog = CatalogController()
对于上述代码,如果此时有这样一个请求:/catalog/books/bestsellers,则pecan首先将这个请求分割成:catalog, books, bestsellers。接下来,pecan将会从root控制器中寻找catalog,找到catalog对象后,pecan会继续在catalog控制器中寻找books,以此类推一直找到bestsellers。如果URL以’/‘结束,那么pecan将会查找最后一个控制器的index方法。
进一步讲,下面的这些请求路径:
└── /
├── /hours
└── /catalog
└── /catalog/books
└── /catalog/books/bestsellers
将会路由给这些控制器方法:
└── RootController.index
├── RootController.hours
└── CatalogController.index
└── BooksController.index
└── BooksController.bestsellers
expose()方法
你可以通过expose()方法告诉pecan一个控制器类中的哪些方法是公开可见的。如果一个控制器类的某个方法没有被expose()装饰,那么pecan不会将请求路由给它。
expose()有很多使用方法。最简单的用法就是不给它传递任何参数。在这种情况下,这个控制器返回一个代表HTML响应体的字符串。如下面的代码所示。
from pecan import expose
class RootController(object):
@expose()
def hello(self):
return 'Hello World’
不过,更常见的用法是指定一个模板和名字空间,如下所示,html_template.mako是一个模板,hello()方法返回的是一个名字空间{‘msg’:’hello!'},该名字空间被用来渲染html_template.mako模板。
from pecan import expose
class RootController(object):
@expose('html_template.mako')
def hello(self):
return {'msg': 'Hello!’}
html_template.mako模板内容:
<!-- html_template.mako -->
<html>
<body>${msg}</body>
</html>
除了HTML模板,pecan也内置一个特殊的json渲染器,它将名字空间渲染进一个json文本,如下所示:
from pecan import expose
class RootController(object):
@expose('json')
def hello(self):
return {'msg': 'Hello!'}
expose()方法也可以被层叠调用,这允许你根据请求内容的不同而生成不同的响应内容。
from pecan import expose
class RootController(object):
@expose('json')
@expose('text_template.mako', content_type='text/plain')
@expose('html_template.mako')
def hello(self):
return {'msg': 'Hello!'}
从这里可以看见,我们用不同的参数分别调用了三次expose()方法。
@expose('json')
第一个调用告诉pecan当客户端请求/hello.json或者http header中包含“Accept: application/json”时,将hello()方法响应的名字空间渲染进json文本。
@expose('text_template.mako', content_type='text/plain')
第二个调用告诉pecan当客户端请求/hello.txt或者http header中包含“Accept: text/plain”时,使用text_template.mako模板文件。
@expose('html_template.mako')
第三个调用告诉pecan当客户端请求/hello.html时,使用html_template.mako模板文件。如果客户端请求/hello,并且没有显式指明内容格式,则pecan默认使用text/html的内容格式进行响应,假设客户端想要HTML。
显式指定路径分段
偶尔,你想在路径里面显式指定分段,例如有这样一个请求:/some-path,由于python语法限制,pecan并不能将该请求的处理方法声明为some-path,即下面的代码在python中是无效的:
class RootController(object):
@pecan.expose()
def some-path(self):
return dict()
为了能绕过这种限制,pecan允许你在expose()装饰器内指定一个路径分段,如下面代码段所示:
class RootController(object):
@pecan.expose(route='some-path')
def some_path(self):
return dict()
在这个例子中,pecan应用将会给/some-path/请求返回HTTP 200,但对于/some_path/请求将会返回HTTP 404。
route()方法也可以被显式的用来作为对expose()方法中route参数的代替,如下面代码段所示:
class RootController(object):
@pecan.expose()
def some_path(self):
return dict()
pecan.route('some-path', RootController.some_path)
更进一步,用同样的方式,还可以利用route()方法来将请求路由给下一级控制器。
class ChildController(object):
@pecan.expose()
def child(self):
return dict()
class RootController(object):
pass
pecan.route(RootController, 'child-path', ChildController())
在这个例子中,pecan应用将会给请求/child-path/child/返回HTTP 200响应。
基于请求方法的路由
expose()方法中的generic参数可以根据请求方法对URL进行重载。在下面的例子中,同一个URL可以被两个不同的方法处理(一个用来处理HTTP GET请求,一个用来处理HTTP POST请求)。当在expose()方法的参数中指定“generic=True”后,对’/‘的GET请求,由index()方法处理,对’/‘的POST请求将会由index_POST()方法处理。
from pecan import expose
class RootController(object):
# HTTP GET /
@expose(generic=True, template='json')
def index(self):
return dict()
# HTTP POST /
@index.when(method='POST', template='json')
def index_POST(self, **kw):
uuid = create_something()
return dict(uuid=uuid)
Pecan的路由算法
有时,标准的对象分发路由方式不足以将某个URL路由到一个控制器上。pecan提供了几种方法去使对象分发方式的路由发生短路,以便用更多的控制来处理URL,以下这些特殊的方法用来实现这个目标:_lookup(),_default(),_route()。在你的控制器上定义这些方法可以让你更加灵活的处理一个URL的全部内容或部分内容。
_lookup()方法
_lookup()提供一种方式处理一个URL的部分内容,并返回一个新的控制器用于处理URL的剩余部分。一个_lookup()方法可以提供一个或多个参数,以及URL的分片。同时_lookup()方法应该用可变的位置表示URL的剩余部分,并且在它的返回值里包含未处理的剩余URL部分。在下面的例子中,对象分发路由算法将会把remainder列表传递给该方法返回的控制器。
_lookup()除了被用来动态创建控制器以外,当没有其他任何控制器方法能够匹配一个URL且没有定义_default()方法时,_lookup()方法作为最后一个方法被调用。
from pecan import expose, abort
from somelib import get_student_by_name
class StudentController(object):
def __init__(self, student):
self.student = student
@expose()
def name(self):
return self.student.name
class RootController(object):
@expose()
def _lookup(self, primary_key, *remainder):
student = get_student_by_primary_key(primary_key)
if student:
return StudentController(student), remainder
else:
abort(404)
对'/8/name’的GET请求将会返回primary_key等于8的学生的名字。
_default()方法
对于标准的对象分发路由机制,当没有其他任何控制器方法能够匹配一个URL时, _default()方法作为最后一个方法被调用。
from pecan import expose
class RootController(object):
@expose()
def english(self):
return 'hello'
@expose()
def french(self):
return 'bonjour'
@expose()
def _default(self):
return 'I cannot say hello in that language'
在上面的例子中,对/spanish的请求将会路由到RootController._default()方法。
_route()方法
_route()方法允许一个控制器完全覆盖pecan的路由机制。pecan它本身也使用_route()方法去实现它的RestController。如果你想在pecan之上定义一套替代的路由机制,那么定义一个包含_route()方法的基控制器将会使你完全掌控请求的路由。