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