Tornado Web应用程序的结构¶
Tornado Web应用程序通常由一个或多个 RequestHandler 子类 Application 将传入请求路由到处理程序的对象,以及 main() 函数来启动服务器。
一个最小的“hello world”示例如下所示:
import asyncio
import tornado.web
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello, world")
def make_app():
return tornado.web.Application([
(r"/", MainHandler),
])
async def main():
app = make_app()
app.listen(8888)
await asyncio.Event().wait()
if __name__ == "__main__":
asyncio.run(main())
这个 Application 对象¶
这个 Application 对象负责全局配置,包括将请求映射到处理程序的路由表。
路由表是 URLSpec 对象(或元组),每个对象至少包含一个正则表达式和一个处理程序类。顺序很重要;使用第一个匹配规则。如果正则表达式包含捕获组,则这些组是 路径参数 并将传递给处理程序的HTTP方法。如果将字典作为 URLSpec ,它提供 初始化参数 将传递给 RequestHandler.initialize . 最后, URLSpec 可能有一个名称,允许它与 RequestHandler.reverse_url .
例如,在这个片段中,根URL / 映射到 MainHandler 和表单的URL /story/ 后跟一个数字映射到 StoryHandler . 这个数字(作为字符串)传递给 StoryHandler.get .
class MainHandler(RequestHandler):
def get(self):
self.write('<a href="%s">link to story 1</a>' %
self.reverse_url("story", "1"))
class StoryHandler(RequestHandler):
def initialize(self, db):
self.db = db
def get(self, story_id):
self.write("this is story %s" % story_id)
app = Application([
url(r"/", MainHandler),
url(r"/story/([0-9]+)", StoryHandler, dict(db=db), name="story")
])
这个 Application 构造函数采用许多关键字参数,这些参数可用于自定义应用程序的行为并启用可选功能;请参见 Application.settings 完整的列表。
子类别化 RequestHandler¶
Tornado Web应用程序的大部分工作都是在 RequestHandler . 处理程序子类的主要入口点是以正在处理的HTTP方法命名的方法: get() , post() 等。每个处理程序可以定义这些方法中的一个或多个来处理不同的HTTP操作。如上所述,将使用与匹配的路由规则的捕获组相对应的参数调用这些方法。
在处理程序中,调用诸如 RequestHandler.render 或 RequestHandler.write 做出回应。 render() 加载A Template 并用给定的参数呈现。 write() 用于非基于模板的输出;它接受字符串、字节和字典(dict将被编码为json)。
许多方法 RequestHandler 在子类中被重写,并在整个应用程序中使用。定义一个 BaseHandler 重写方法的类,例如 write_error 和 get_current_user 然后把你自己的 BaseHandler 而不是 RequestHandler 对于所有特定的处理程序。
处理请求输入¶
请求处理程序可以访问表示当前请求的对象 self.request . 参见类定义 HTTPServerRequest 获取完整的属性列表。
HTML表单使用的格式中的请求数据将为您进行分析,并在以下方法中可用 get_query_argument 和 get_body_argument .
class MyFormHandler(tornado.web.RequestHandler):
def get(self):
self.write('<html><body><form action="/myform" method="POST">'
'<input type="text" name="message">'
'<input type="submit" value="Submit">'
'</form></body></html>')
def post(self):
self.set_header("Content-Type", "text/plain")
self.write("You wrote " + self.get_body_argument("message"))
由于HTML表单编码对于参数是单个值还是包含一个元素的列表不明确, RequestHandler 具有不同的方法以允许应用程序指示是否需要列表。对于列表,使用 get_query_arguments 和 get_body_arguments 而不是它们的单一对应物。
通过表单上载的文件可在 self.request.files ,它映射名称(HTML的名称 <input type="file"> 元素)到文件列表。每个文件都是表单的字典 {{"filename":..., "content_type":..., "body":...}} . 这个 files 只有使用表单包装器(即 multipart/form-data 内容类型);如果未使用此格式,则可以在 self.request.body . 默认情况下,上载的文件在内存中是完全缓冲的;如果您需要处理过大的文件而无法在内存中舒适地保存,请参阅 stream_request_body 类修饰器。
在演示目录中, file_receiver.py 显示两种接收文件上载的方法。
由于HTML表单编码的特殊性(例如,单参数和复数参数之间的模糊性),Tornado不尝试将表单参数与其他类型的输入统一起来。特别是,我们不解析JSON请求体。希望使用JSON而不是表单编码的应用程序可能会重写 prepare 分析他们的请求:
def prepare(self):
if self.request.headers.get("Content-Type", "").startswith("application/json"):
self.json_args = json.loads(self.request.body)
else:
self.json_args = None
重写RequestHandler方法¶
除了 get()/post() /等,某些其他方法 RequestHandler 在必要时被子类覆盖。对于每个请求,都会发生以下调用序列:
一个新的
RequestHandler对象是根据每个请求创建的。initialize()使用来自的初始化参数调用Application配置。initialize通常只应保存传递给成员变量的参数;它可能不会生成任何输出或调用方法,如send_error.prepare()被称为。这在所有处理程序子类共享的基类中最有用,如prepare无论使用哪种HTTP方法都会调用。prepare可能产生输出;如果它调用finish(或)redirect等),此处停止处理。其中一个HTTP方法称为:
get(),post(),put()等等。如果URL正则表达式包含捕获组,则将它们作为参数传递给此方法。请求完成后,
on_finish()被称为。这通常是在get()或者返回另一个HTTP方法。
设计为重写的所有方法在 RequestHandler 文档。一些最常见的重写方法包括:
write_error-输出用于错误页的HTML。on_connection_close-当客户端断开连接时调用;应用程序可以选择检测此情况并停止进一步处理。请注意,不能保证可以及时检测到闭合连接。get_current_user-见 用户身份验证 .get_user_locale-收益Locale对象以供当前用户使用。set_default_headers-可用于在响应上设置其他头(例如自定义Server标题)。
错误处理¶
如果处理程序引发异常,Tornado将调用 RequestHandler.write_error 生成错误页。 tornado.web.HTTPError 可用于生成指定的状态代码;所有其他异常都返回500状态。
默认错误页包括调试模式下的堆栈跟踪和错误的一行描述(例如“500:内部服务器错误”),否则。要生成自定义错误页,请重写 RequestHandler.write_error (可能在所有处理程序共享的基类中)。这种方法可以通过如下方法正常产生输出 write 和 render . 如果错误是由异常引起的,则 exc_info Triple将作为关键字参数传递(请注意,此异常不保证是中的当前异常 sys.exc_info 如此 write_error 必须使用例如 traceback.format_exception 而不是 traceback.format_exc )
还可以从常规处理程序方法生成错误页,而不是 write_error 通过呼叫 set_status ,写一个响应,然后返回。特殊例外 tornado.web.Finish 可以在不调用的情况下引发以终止处理程序 write_error 在简单返回不方便的情况下。
对于404个错误,请使用 default_handler_class Application setting . 此处理程序应重写 prepare 而不是像 get() 所以它适用于任何HTTP方法。它应该产生上面描述的错误页:或者通过 HTTPError(404) 压倒一切 write_error 或呼叫 self.set_status(404) 直接产生反应 prepare() .
重定向¶
在Tornado中,有两种主要方法可以重定向请求: RequestHandler.redirect 并与 RedirectHandler .
你可以使用 self.redirect() 在一个 RequestHandler 方法将用户重定向到其他位置。还有一个可选参数 permanent 它可以用来表示重定向被认为是永久的。默认值为 permanent 是 False ,生成一个 302 Found HTTP响应代码,适用于成功后重定向用户 POST 请求。如果 permanent 是 True , the 301 Moved Permanently 使用HTTP响应代码,这对于以SEO友好的方式重定向到页面的规范URL非常有用。
RedirectHandler 允许您直接在 Application 路由表。例如,要配置单个静态重定向,请执行以下操作:
app = tornado.web.Application([
url(r"/app", tornado.web.RedirectHandler,
dict(url="http://itunes.apple.com/my-app-id")),
])
RedirectHandler 还支持正则表达式替换。以下规则重定向所有以开头的请求 /pictures/ 前缀 /photos/ 取而代之的是:
app = tornado.web.Application([
url(r"/photos/(.*)", MyPhotoHandler),
url(r"/pictures/(.*)", tornado.web.RedirectHandler,
dict(url=r"/photos/{0}")),
])
不像 RequestHandler.redirect , RedirectHandler 默认情况下使用永久重定向。这是因为路由表在运行时不会更改,并且假定是永久的,而在处理程序中找到的重定向可能是其他可能更改的逻辑的结果。使用 RedirectHandler ,添加 permanent=False 到 RedirectHandler 初始化参数。
异步处理程序¶
某些处理方法(包括 prepare() and the HTTP verb methods get()/post() /etc)可以重写为协程以使处理程序异步。
例如,这里有一个使用协程的简单处理程序:
class MainHandler(tornado.web.RequestHandler):
async def get(self):
http = tornado.httpclient.AsyncHTTPClient()
response = await http.fetch("http://friendfeed-api.com/v2/feed/bret")
json = tornado.escape.json_decode(response.body)
self.write("Fetched " + str(len(json["entries"])) + " entries "
"from the FriendFeed API")
有关更高级的异步示例,请查看 chat example application ,它使用 long polling . 长轮询的用户可能希望覆盖 on_connection_close() 在客户端关闭连接后进行清理(但请参阅该方法的docstring以了解警告)。