Tornado 4.3
於2015年11月6日發佈,該版本正式支持Python3.5
的async
/await
關鍵字,而且用舊版本CPython編譯Tornado一樣可使用這兩個關鍵字,這無疑是一種進步。其次,這是最後一個支持Python2.6
和Python3.2
的版本了,在後續的版本了會移除對它們的兼容。如今網絡上尚未Tornado4.3
的中文文檔,因此爲了讓更多的朋友能接觸並學習到它,我開始了這個翻譯項目,但願感興趣的小夥伴能夠一塊兒參與翻譯,項目地址是tornado-zh on Github,翻譯好的文檔在Read the Docs上直接能夠看到。歡迎Issues or PR。html
一般一個Tornado web應用包括一個或者多個RequestHandler
子類,一個能夠將收到的請求路由到對應handler的Application
對象,和一個啓動服務的 main()
函數.python
一個最小的"hello world"例子就像下面這樣:git
import tornado.ioloop import tornado.web class MainHandler(tornado.web.RequestHandler): def get(self): self.write("Hello, world") def make_app(): return tornado.web.Application([ (r"/", MainHandler), ]) if __name__ == "__main__": app = make_app() app.listen(8888) tornado.ioloop.IOLoop.current().start()
Application
對象Application
對象是負責全局配置的,包括映射請求轉發給處理程序的路由表.github
路由表是URLSpec
對象(或元組)的列表, 其中每一個都包含(至少)一個正則表達式和一個處理類. 順序問題; 第一個匹配的規則會被使用. 若是正則表達式包含捕獲組, 這些組會被做爲 路徑參數 傳遞給處理函數的HTTP方法.若是一個字典做爲 URLSpec
的第三個參數被傳遞, 它會做爲 初始參數傳遞給 RequestHandler.initialize
. 最後 URLSpec
可能有一個名字(name), 這將容許它被 RequestHandler.reverse_url
使用.web
例如, 在這個片斷中根URL /
映射到了MainHandler
, 像 /story/
後跟着一個數字這種形式的URL被映射到了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
.json
RequestHandler
子類Tornado web 應用程序的大部分工做是在RequestHandler
子類下完成的.處理子類的主入口點是一個命名爲處理HTTP方法的函數: get()
,post()
, 等等. 每一個處理程序能夠定義一個或者多個這種方法來處理不一樣的HTTP動做. 如上所述, 這些方法將被匹配路由規則的捕獲組對應的參數調用.api
在處理程序中, 調用方法如RequestHandler.render
或者RequestHandler.write
產生一個響應. render()
經過名字加載一個Template
並使用給定的參數渲染它. write()
被用於非模板基礎的輸出; 它接受字符串, 字節, 和字典(字典會被編碼成JSON).瀏覽器
在RequestHandler
中的不少方法的設計是爲了在子類中複寫和在整個應用中使用. 經常使用的方法是定義一個 BaseHandler
類, 複寫一些方法例如RequestHandler.write_error
和RequestHandler.get_current_user
而後子類繼承使用你本身的 BaseHandler
而不是RequestHandler
在你全部具體的處理程序中.緩存
處理請求的程序(request handler)可使用 self.request
訪問表明當前請求的對象. 經過tornado.httputil.HTTPServerRequest
的類定義查看完整的屬性列表.
使用HTML表單格式請求的數據會被解析而且能夠在一些方法中使用, 例如RequestHandler.get_query_argument
和RequestHandler.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"))
因爲HTLM表單編碼不肯定一個標籤的參數是單一值仍是一個列表,RequestHandler
有明確的方法來容許應用程序代表是否它指望接收一個列表.對於列表, 使用RequestHandler.get_query_arguments
和RequestHandler.get_body_arguments
而不是它們的單數形式.
經過一個表單上傳的文件可使用 self.request.files
,它遍歷名字(HTML 標籤 <input type="file">
的name)到一個文件列表.每一個文件都是一個字典的形式{"filename":..., "content_type":..., "body":...}
. files
對象是當前惟一的若是文件上傳是經過一個表單包裝(i.e. a multipart/form-data
Content-Type); 若是沒用這種格式,原生上傳的數據能夠調用 self.request.body
使用.默認上傳的文件是徹底緩存在內存中的; 若是你須要處理佔用內存太大的文件能夠看看 stream_request_body
類裝飾器.
因爲HTML表單編碼格式的怪異 (e.g. 在單數和複數參數的含糊不清), Tornado不會試圖統一表單參數和其餘輸入類型的參數. 特別是, 咱們不解析JSON請求體.應用程序但願使用JSON代替表單編碼能夠複寫 RequestHandler.prepare
來解析它們的請求:
def prepare(self): if self.request.headers["Content-Type"].startswith("application/json"): self.json_args = json.loads(self.request.body) else: self.json_args = None
除了 get()
/post()
/等, 在 .RequestHandler
中的某些其餘方法被設計成了在必要的時候讓子類重寫. 在每一個請求中, 會發生下面的調用序列:
在每次請求時生成一個新的 RequestHandler
對象
RequestHandler.initialize()
被 Application
配置中的初始化參數被調用. initialize
一般應該只保存成員變量傳遞的參數; 它不可能產生任何輸出或者調用方法, 例如RequestHandler.send_error
.
RequestHandler.prepare()
被調用. 這在你全部處理子類共享的基類中是最有用的, 不管是使用哪一種HTTP方法, prepare
都會被調用.prepare
可能會產生輸出; 若是它調用 RequestHandler.finish
(或者 redirect
, 等), 處理會在這裏結束.
其中一種HTTP方法被調用: get()
, post()
, put()
,等. 若是URL的正則表達式包含捕獲組, 它們會被做爲參數傳遞給這個方法.
當請求結束, RequestHandler.on_finish()
方法被調用. 對於同步處理程序會在 get()
(等)後當即返回; 對於異步處理程序,會在調用RequestHandler.finish()
後返回.
全部這樣設計被用來複寫的方法被記錄在了RequestHandler
的文檔中.其中最經常使用的一些被複寫的方法包括:
RequestHandler.write_error
- 輸出對錯誤頁面使用的HTML.
RequestHandler.on_connection_close
- 當客戶端斷開時被調用;應用程序能夠檢測這種狀況,並中斷後續處理. 注意這不能保證一個關閉的鏈接及時被發現.
RequestHandler.get_current_user
- 參考 user-authentication
RequestHandler.get_user_locale
- 返回 .Locale
對象給當前
用戶使用
RequestHandler.set_default_headers
- 能夠被用來設置額外的響應
頭(例如自定義的 Server
頭)
若是一個處理程序拋出一個異常, Tornado會調用RequestHandler.write_error
來生成一個錯誤頁.tornado.web.HTTPError
能夠被用來生成一個指定的狀態碼; 全部其餘的異常都會返回一個500狀態.
默認的錯誤頁面包含一個debug模式下的調用棧和另一行錯誤描述(e.g. "500: Internal Server Error"). 爲了建立自定義的錯誤頁面, 複寫RequestHandler.write_error
(可能在一個全部處理程序共享的一個基類裏面).這個方法可能產生輸出一般經過一些方法, 例如 RequestHandler.write
和RequestHandler.render
. 若是錯誤是由異常引發的, 一個 exc_info
將做爲一個關鍵字參數傳遞(注意這個異常不能保證是 sys.exc_info
當前的異常, 因此 write_error
必須使用 e.g. traceback.format_exception
代替traceback.format_exc
).
也能夠在常規的處理方法中調用 RequestHandler.set_status
代替write_error
返回一個(自定義)響應來生成一個錯誤頁面. 特殊的例外tornado.web.Finish
在直接返回不方便的狀況下可以在不調用 write_error
前結束處理程序.
對於404錯誤, 使用 default_handler_class
Application setting
. 這個處理程序會複寫RequestHandler.prepare
而不是一個更具體的方法, 例如 get()
因此它能夠在任何HTTP方法下工做. 它應該會產生如上所說的錯誤頁面: 要麼raise一個 HTTPError(404)
要麼複寫 write_error
, 或者調用self.set_status(404)
或者在 prepare()
中直接生成響應.
這裏有兩種主要的方式讓你能夠在Tornado中重定向請求:RequestHandler.redirect
和使用 RedirectHandler
.
你能夠在一個 RequestHandler
的方法中使用 self.redirect()
把用戶重定向到其餘地方. 還有一個可選參數 permanent
你可使用它來代表這個重定向被認爲是永久的. permanent
的默認值是 False
, 這會生成一個302 Found
HTTP響應狀態碼, 適合相似在用戶的 POST
請求成功後的重定向.若是 permanent
是true, 會使用 301 Moved Permanently
HTTP響應, 更適合e.g. 在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/\1")), ])
不像 RequestHandler.redirect
, RedirectHandler
默認使用永久重定向.這是由於路由表在運行時不會改變, 並且被認爲是永久的.當在處理程序中發現重定向的時候, 多是其餘可能改變的邏輯的結果.用 .RedirectHandler
發送臨時重定向, 須要添加 permanent=False
到.RedirectHandler
的初始化參數.
Tornado默認會同步處理: 當 get()
/post()
方法返回, 請求被認爲結束而且返回響應. 由於當一個處理程序正在運行的時候其餘全部請求都被阻塞,任何須要長時間運行的處理都應該是異步的, 這樣它就能夠在非阻塞的方式中調用它的慢操做了. 這個話題更詳細的內容包含在async
中; 這部分是關於在 RequestHandler
子類中的異步技術的細節.
使用 coroutine
裝飾器是作異步最簡單的方式. 這容許你使用 yield
關鍵字執行非阻塞I/O, 而且直到協程返回才發送響應. 查看 coroutines
瞭解更多細節.
在某些狀況下, 協程不如回調爲主的風格方便, 在這種狀況下tornado.web.asynchronous
裝飾器能夠用來代替. 當使用這個裝飾器的時候,響應不會自動發送; 而請求將一直保持開放直到callback調用RequestHandler.finish
. 這須要應用程序確保這個方法被調用或者其餘用戶的瀏覽器簡單的掛起.
這裏是一個使用Tornado's 內置的 AsyncHTTPClient
調用FriendFeed API的例
子:
class MainHandler(tornado.web.RequestHandler): @tornado.web.asynchronous def get(self): http = tornado.httpclient.AsyncHTTPClient() http.fetch("http://friendfeed-api.com/v2/feed/bret", callback=self.on_response) def on_response(self, response): if response.error: raise tornado.web.HTTPError(500) json = tornado.escape.json_decode(response.body) self.write("Fetched " + str(len(json["entries"])) + " entries " "from the FriendFeed API") self.finish()
當 get()
返回, 請求尚未完成. 當HTTP客戶端最終調用on_response()
, 這個請求仍然是開放的, 響應最終刷到客戶端經過調用 self.finish()
.
爲了方便對比, 這裏有一個使用協程的相同的例子:
class MainHandler(tornado.web.RequestHandler): @tornado.gen.coroutine def get(self): http = tornado.httpclient.AsyncHTTPClient() response = yield 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)的AJAX聊天室.使用長輪詢的用戶可能想要覆蓋 on_connection_close()
來在客戶端關閉鏈接以後進行清理(注意看方法的文檔來查看警告).