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方法是最后被调用的。

结束!

posted @ 2021-06-01 17:29  aaronthon  阅读(252)  评论(0编辑  收藏  举报