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

結束!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章