補充: 如果最后要求所有沒有匹配到的路由跳轉到某一頁或handler去, 怎么做
( r'/.*', ShowAdminHandler), 完美
在Tornado中實現Django分層路由
看了幾個 Tornado 的 demo 發現都是一個py搞定所有,這樣做做為例子來講確實很直觀,但如果在大型項目中這樣做肯定不現實,所以博主考慮如何制定合理的 Tornado 目錄。
我認為 Django 的目錄有很多可取之處,然而在進行分層的時候就遇到了第一個問題:路由分層。看了 Tornado 的源碼和文檔發現 Tornado 官方並沒有提供分層路由的實現,於是接着搜索 gayhub 😆, 發現有個“tornado-routes” 的項目。經過閱讀發現此項目確實豐富了 Tornado 的路由,也考慮到了分層路由,但其使用的是直接在類中添加修飾器的方式做路由,這和 Django 分層路由大相徑庭,同時感覺它的實現方式也略為繁瑣,因此我就排除了這個方案。之后又瀏覽了一些相關的 repo ,並沒有再找到考慮分層路由的包。既然沒人造這個輪子博主自己造好啦。
首先要做的就是了解 Tornado 是如何處理路由的。 Tornado 的啟動文件一般是這樣的:
app = tornado.web.Application( [ (r"/", MainHandler), (r"/a/message/new", MessageNewHandler), (r"/a/message/updates", MessageUpdatesHandler), ] ) app.listen(options.port) tornado.ioloop.IOLoop.current().start()
可以直觀的看到路由都在 tornado.web.Application()
的第一個 list 里,所以直接去看 Tornado 源碼里 tornado.web.Application
的實現:
def __init__(self, handlers=None, default_host="", transforms=None, **settings): ... if handlers: self.add_handlers(".*$", handlers) ...
從初始化方法中我們知道了路由 list 在 Application 里叫 handlers
, 其中self.add_handlers()
就是 Tornado 處理路由的關鍵。
然后來看add_handlers(self, host_pattern, host_handlers)
方法:
def add_handlers(self, host_pattern, host_handlers): """Appends the given handlers to our handler list. Host patterns are processed sequentially in the order they were added. All matching patterns will be considered. """ if not host_pattern.endswith("$"): host_pattern += "$" handlers = [] # The handlers with the wildcard host_pattern are a special # case - they're added in the constructor but should have lower # precedence than the more-precise handlers added later. # If a wildcard handler group exists, it should always be last # in the list, so insert new groups just before it. if self.handlers and self.handlers[-1][0].pattern == '.*$': self.handlers.insert(-1, (re.compile(host_pattern), handlers)) else: self.handlers.append((re.compile(host_pattern), handlers)) for spec in host_handlers: if isinstance(spec, (tuple, list)): assert len(spec) in (2, 3, 4) spec = URLSpec(*spec) handlers.append(spec) if spec.name: if spec.name in self.named_handlers: app_log.warning( "Multiple handlers named %s; replacing previous value", spec.name) self.named_handlers[spec.name] = spec
可以看到此方法接受的是一個 tuple 的 list 並通過處理返回一個 URLSpec() 的 list, 那么其實只要把分層路由信息統一成一個 tuple 的 list 傳給 Application就可以實現分層路由的實現。
為了實現統一分層路由需要寫兩個方法: 一個是 include(url_module)
引入子層路由信息統一輸出,另一個 url_wrapper(urls)
則是將分層、不分層信息統一格式化成一個 tuple 的 list, 具體實現:
import tornado.httpserver import tornado.ioloop import tornado.web from url_router import include, url_wrapper from importlib import import_module def include(module): res = import_module(module) urls = getattr(res, 'urls', res) return urls def url_wrapper(urls): wrapper_list = [] for url in urls: path, handles = url if isinstance(handles, (tuple, list)): for handle in handles: pattern, handle_class = handle wrap = ('{0}{1}'.format(path, pattern), handle_class) wrapper_list.append(wrap) else: wrapper_list.append((path, handles)) return wrapper_list application = tornado.web.Application(url_wrapper([ (r"/test/", include('test.urls')), (r"/other", XXXHandle), ])) if __name__ == "__main__": http_server = tornado.httpserver.HTTPServer(application) http_server.listen(8888) tornado.ioloop.IOLoop.instance().start()
#test/urls.py coding: utf-8 from __future__ import unicode_literals from test.views import TestWriteHandle, TestHandle urls = [ (r'write', TestWriteHandle), (r'', TestHandle), ]
然后運行 Tornado 服務器,分別打開 http://localhost:8888/test/write
和 http://localhost:8888/other
測試可用性。
一個簡單的 Django 分層路由就可以在 Tornado 中使用啦。
原文地址:https://www.rapospectre.com/blog/13