摘要:譯者說于年月日發布,該版本正式支持的關鍵字,并且用舊版本編譯同樣可以使用這兩個關鍵字,這無疑是一種進步。其次,這是最后一個支持和的版本了,在后續的版本了會移除對它們的兼容。
譯者說
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。
Tornado web應用的結構通常一個Tornado web應用包括一個或者多個RequestHandler 子類,一個可以將收到的請求路由到對應handler的Application 對象,和一個啟動服務的 main() 函數.
一個最小的"hello world"例子就像下面這樣:
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對象是負責全局配置的,包括映射請求轉發給處理程序的路由表.
路由表是URLSpec對象(或元組)的列表, 其中每個都包含(至少)一個正則表達式和一個處理類. 順序問題; 第一個匹配的規則會被使用. 如果正則表達式包含捕獲組, 這些組會被作為 路徑參數 傳遞給處理函數的HTTP方法.如果一個字典作為 URLSpec 的第三個參數被傳遞, 它會作為 初始參數傳遞給 RequestHandler.initialize. 最后 URLSpec 可能有一個名字(name), 這將允許它被 RequestHandler.reverse_url 使用.
例如, 在這個片段中根URL / 映射到了MainHandler , 像 /story/ 后跟著一個數字這種形式的URL被映射到了StoryHandler. 這個數字被傳遞(作為字符串)給StoryHandler.get.
class MainHandler(RequestHandler): def get(self): self.write("link to story 1" % 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() 通過名字加載一個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("") 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 標簽 的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復寫RequestHandler的方法
除了 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() 來在客戶端關閉連接之后進行清理(注意看方法的文檔來查看警告).
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/37705.html
摘要:譯者說于年月日發布,該版本正式支持的關鍵字,并且用舊版本編譯同樣可以使用這兩個關鍵字,這無疑是一種進步。其次,這是最后一個支持和的版本了,在后續的版本了會移除對它們的兼容。 譯者說 Tornado 4.3于2015年11月6日發布,該版本正式支持Python3.5的async/await關鍵字,并且用舊版本CPython編譯Tornado同樣可以使用這兩個關鍵字,這無疑是一種進步。其次...
摘要:譯者說于年月日發布,該版本正式支持的關鍵字,并且用舊版本編譯同樣可以使用這兩個關鍵字,這無疑是一種進步。其次,這是最后一個支持和的版本了,在后續的版本了會移除對它們的兼容。 譯者說 Tornado 4.3于2015年11月6日發布,該版本正式支持Python3.5的async/await關鍵字,并且用舊版本CPython編譯Tornado同樣可以使用這兩個關鍵字,這無疑是一種進步。其次...
閱讀 3741·2021-11-24 09:39
閱讀 3472·2019-08-30 15:56
閱讀 1375·2019-08-30 15:55
閱讀 1040·2019-08-30 15:53
閱讀 1927·2019-08-29 18:37
閱讀 3608·2019-08-29 18:32
閱讀 3135·2019-08-29 16:30
閱讀 2936·2019-08-29 15:14