Pecan控制器和路由系统
Pecan路由采用的是对象分发机制,将HTTP请求分发到控制器,然后到控制器里定义的方法。
对象分发机制将请求路径进行切割,根据请求路径从root控制器开始,按次序寻找路径对应的控制器及方法。
一、控制器和路由实例
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()
1, 访问/时,会由RootController类的index方法进行响应;
2, 访问/hours或者/hours/时,会由RootController类的hours方法进行响应;
3, 访问/catalog时,首先会通过RootController找到其catalog属性,也就是一个CatalogController实例,然后寻找CatalogController类的index方法进行响应;
4, 访问/catalog/books时,首先会通过RootController找到其catalog属性,也就是一个CatalogController实例,然后寻找CatalogController类的books属性,也就是一个BookController实例,使用其index方法进行响应;
5, 访问/catalog/books/bestsellers时,首先会通过RootController找到其catalog属性,也就是一个CatalogController实例,然后寻找CatalogController类的books属性,也就是一个BookController实例,使用其bestseller方法进行响应.
二、expose 暴露控制器方法
expose告诉Pecan类中的哪些方法是公开可见的 。如果一个方法没有用修饰expose(),Pecan永远不会将请求路由到它。
pecan默认采用expose进行路由绑定,需要路由控制器类的方法都要经过expose装饰器的装饰,pecan就可以使HTTP请求找到对应的方法。
不同的使用方法会有不同的效果,如下:
2.1 expose()
from pecan import expose class RootController(object): @expose() def hello(self): return 'Hello World’
被装饰的方法需要返回一个字符串,表示HTML响应的body。
2.2 @expose(html_template_name)
from pecan import expose class RootController(object): @expose('html_template.mako') def hello(self): return {'msg': 'Hello!’}
<!-- html_template.mako --> <html> <body>${msg}</body> </html>
被装饰的方法返回一个字典,字典的key可以在html模板中使用${key}
的方式引用。
2.3 @expose(route='some-path')
例如有这样一个请求:/some-path,由于python语法限制,pecan并不能将该请求的处理方法声明为some-path。使用@expose(route='some-path'),被装饰方法将响应/some-path
请求。
class RootController(object): @expose(route='some-path') def some_path(self): return dict()
注意:尽量不使用dict(),使用不当,HTTP状态码将是204,及服务器没有返回任何内容错误。
另一种方式:pecan.route()
class RootController(object): @expose() def some_path(self): return dict() pecan.route('some-path', RootController.some_path)
延伸:利用route()方法来将请求路由给下一级控制器
class ChildController(object): @expose() def child(self): return dict() class RootController(object): pass pecan.route(RootController, 'child-path', ChildController())
在这个例子中,pecan应用将会给请求/child-path/child/返回HTTP 200响应。
2.4 @expose(generic=True)
expose()方法中的generic参数可以根据请求方法对URL进行重载,即一个url路径可以被两个不同的方法处理。
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)
对于"/"的GET请求,由index()方法处理;对于"/"的POST请求,由index_POST方法处理。
2.5 @expose()叠加用法
class RootController(object): @expose('json') @expose('text_template.mako', content_type='text/plain') @expose('html_template.mako') def hello(self): return {'msg': 'Hello!'}
叠加使用后一个hello
方法可以响应三种格式的请求(application/json, text/plain, text/html)。
当客户端请求/hello.json或者http header中包含“Accept: application/json”时,将hello()方法响应的名字空间渲染进json文本,及@expose('json')用法;
当客户端请求/hello.txt或者http header中包含“Accept: text/plain”时,使用text_template.mako模板文件响应,即@expose('text_template.mako', content_type='text/plain')用法;
当客户端请求/hello.html时,使用html_template.mako模板文件。如果客户端请求/hello,并且没有显式指明内容格式,则pecan默认使用text/html的内容格式进行响应,假设客户端想要HTML。
2.6 _lookup()
_lookup
方法是最后尝试的方法,只有没有控制器可以响应请求的URL,而且最后找到的控制器没有定义_default
方法时,采用_lookup。
此方法返回一个新的控制器用于控制url的剩余部分,如下代码:
def get_student_by_primary_key(num): a = ["xiao_ming", "xiao_li"] num = int(num) if type(num) == int and len(a) > int(num) else 0 return a[num] class Addr(object): @expose() def index(self): return "addr" class StudentController(object): def __init__(self, student): self.student = student @expose() def name(self): return self.student addr = Addr() 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: return "404"
例如"/1/name"路径,将走_lookup()方法,返回 StudentController(student), remainder;
此时,StudentController(student) 是一个新的控制器,而remainder是url的剩余部分,即 name;
StudentController() 控制器将找到def name(self) 进而响应请求。
同理,对于"/100/addr",也走_lookup()方法,返回 StudentController(student), remainder;
此时,StudentController(student) 是一个新的控制器,而remainder是url的剩余部分,即 addr;
找到addr = Addr(),进而得到响应。
2.7 _default()
对于标准的对象路由分发机制,当没有任何控制器可以处理url是,_default将要作为最后一个方法被调度。
from functools import wraps def happy_expose(f=None, **kw): if f is None: def inner_expose(func): return happy_expose(func, **kw) return inner_expose else: @wraps(f) @expose(**kw) def _expose(*args, **kwargs): return f(*args, **kwargs) return _expose
class RootController(object): @expose() def hours(self): return "Open 24/7 on the web." @happy_expose def _default(self, *remainder): return 'Hello World from root default'
_default方法是最后被调用的。
结束!