当初之所以突然捡起python以及体验tornado,源自忘了哪里看到的一篇文章,说tornado十一个简洁高效的web server以及框架,总共代码两千多行,所以懒人义无反顾的抱起来研究。
上次说的那个Options还好,不小心打开了web.py,尼嘛,光这个一个文件就超过两千行(2.4.1),坑爹啊。。。谁乱说的来着?
考虑到这个文件尼嘛太大了,所以分开研究,依旧是看点儿写点儿。。。所以这一篇只记录Application这个类。
基本上所有的tornado的入门的文章都逃不出这个例子,当然,这篇也不能例外:
application = web.Application([ (r"/", MainPageHandler), ]) http_server = httpserver.HTTPServer(application) http_server.listen(8080) ioloop.IOLoop.instance().start()
很明显,在这个例子中tornado依赖于Application类构造函数中的参数来构建路由映射规则,那这个参数有几个特点:
- 列表
- 每一个列表项都是一个元组
- 每一个元组至少包含了两个参数(为什么说至少,是因为其实还可以包含第三个参数)
- 元组的第一项是一个正则表达式,用来定义路由的filter
- 元组的第二项是一个类,tornado会用这个类的实例来处理相应的路由映射
所以,按照上面的分析,很容易根据需要定义出自己的路由规则表,借用tornado中blog的例子:
(r"/", HomeHandler), (r"/archive", ArchiveHandler), (r"/feed", FeedHandler), (r"/entry/([^/]+)", EntryHandler), (r"/compose", ComposeHandler), (r"/auth/login", AuthLoginHandler), (r"/auth/logout", AuthLogoutHandler),
根据传说中的common sense,所有路由规则中包含wildcard字符的路由规则项,他们在tornado处理的时候,会赋给较低的优先级,毕竟我们不想(.*)$这样的路由规则抢了其他所有人的饭碗,就像这个。。。。。
当然,对于好学的我必须很好奇,除了这个参数之外,还有其他的可选项嘛?所以来看看Application类的构造函数,不过具体的代码和分析就不写了,太长。。。要打好多字。。。
Application构造函数接受的其他参数:
- default_host。当tornado接受到request但是没有指定handler或者没有能够匹配的handler的时候,使用default_host做自动跳转。(不过看了一下Application的构造函数,总觉得不科学啊。。唯一能够触发这种场景的例子貌似就是一个handler都不加,否则想像不出来神码东西可以逃出.*$这样的黑洞。。。)
- transforms。也不知道干啥的
- wsgi。更不知道干啥的。。。
- settings。这个我知道,可以包含对于Application这个web server的配置项,这个太长,放到下一个小标题讲。
默认Application会处理的Settings项:
- gzip。一旦指定gzip,会自动添加一个GZipContentEncoding,用来处理被GZip压缩过的数据。
- ui_modules。不知到是干啥的。。。不过tornado会自己自动添加三个UI Modules:linkify, xsrf_form_html, Template。
- ui_methods。尼嘛还是不知到是干嘛地。。。。
- debug。用来保证tornado运行在debug模式
- static_path。服务器端存放静态文件(图像,CSS,etc.)的相对目录。如果指定了static_path,tornado就会接着在settings中寻找其他几个配置项,包含:
static_url_prefix: 从当前位置指向包含静态文件的目录的“相对路径”
static_handler_class: 类,其实例用来处理客户端对于静态文件的请求
static_handler_args: 字典,包含用来初始化static_handler_class实例,所需要的参数
需要注意的是,不管你愿不愿意,tornado都会帮你自动添加如下四个路由映射规则:
- $static_path/(.*) ---> static_handler_class
- /favicon.icon ---> static_handler_class
- /robots.txt ---> static_handler_class
- .*$ --->所有传入Application构造函数的handler
--------------------------------------------------------------------------------------------------------------2013-1-24
补充一下,在handler中提到,还可以定义以下的settings项:
if "template_loader" in settings: return settings["template_loader"] kwargs = {} if "autoescape" in settings: # autoescape=None means "no escaping", so we have to be sure # to only pass this kwarg if the user asked for it. kwargs["autoescape"] = settings["autoescape"]
---------------------------------------------------------------------------------------------------------------------------------------------------------
嗯,默认的行为就是这样了,如果你有特别的需求,可以自定义一个Application,通过继承默认的类Application。还是来自blog的例子:
class Application(tornado.web.Application): def __init__(self): handlers = [ (r"/", HomeHandler), (r"/archive", ArchiveHandler), (r"/feed", FeedHandler), (r"/entry/([^/]+)", EntryHandler), (r"/compose", ComposeHandler), (r"/auth/login", AuthLoginHandler), (r"/auth/logout", AuthLogoutHandler), ] settings = dict( blog_title=u"Tornado Blog", template_path=os.path.join(os.path.dirname(__file__), "templates"), static_path=os.path.join(os.path.dirname(__file__), "static"), ui_modules={"Entry": EntryModule}, xsrf_cookies=True, cookie_secret="__TODO:_GENERATE_YOUR_OWN_RANDOM_VALUE_HERE__", login_url="/auth/login", autoescape=None, ) tornado.web.Application.__init__(self, handlers, **settings) # Have one global connection to the blog DB across all handlers self.db = tornado.database.Connection( host=options.mysql_host, database=options.mysql_database, user=options.mysql_user, password=options.mysql_password)
上面高亮的部分,或者会被tornado其他的地方用到 (比如xsrf_cookies),或者会被用户自己的代码用到(比如blog_title),反正构造函数不管就是了。
构造函数了解了,然后顺便看看Application类的其他方法们:
- listen。根据给出的参数启动一个HTTPServer,关于这个方法,有两个注意事项:1. For advanced uses (e.g. preforking), do not use this method; create an HTTPServer and call its bind/start methods directly. 2. Note that after calling this method you still need to call IOLoop.instance().start() to start the server.
- add_handler。如果你在Application初始化之后后悔了,想要添加其他的路由映射关系,那就调用这个方法吧!根据上面所说的,含有wildcard字符的路由映射会拥有较低的优先级(其实Application会维护一个handler的列表,每次在规则表中查找映射的时候总是从第一个开始依次遍历,所谓低优先级,就是处在列表尾端而已)
- add_transform。Again,不知道干啥地。。。
- _get_host_handlers。顾名思义,如果目有找到,返回None。
- _load_ui_methods。貌似是从一个或一些UIModule里面把所有的uimethod导入到Application类实例中的self.ui_methods里面。不过不知道意义何在。。。
- _load_ui_modules。把一个或一些UIModule备份在Application类实例中的self.ui_modules里面。也不知道干啥用。。。收藏癖?
- __call__。这个好,在第一个提到的listen方法里面,会把当前application实例(调用listen的那个)作为参数传入HttpServer,既然Application知道的太多,HttpServer显然会把它往死了用。基本上HttpServer自己只管在门口拉客放风,客人来了具体怎么找到对客人胃口的小姐那就是Application这个老鹞的事情了(主要逻辑就是__call__函数中)。。。。而具体办事,老鹞是不亲自上阵的(客人想也不行!),客人满意不满意完全依赖于各位小姐(各个Handler)自己的本事。。。
- reverse_url。不懂
- log_request。这还用说么?我觉得还是要说。主要是,开发者可以通过传入Application的Settings来自定义log方法;否则使用默认的logging。