补充: 如果最后要求所有没有匹配到的路由跳转到某一页或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