摘要:譯者說于年月日發(fā)布,該版本正式支持的關(guān)鍵字,并且用舊版本編譯同樣可以使用這兩個(gè)關(guān)鍵字,這無疑是一種進(jìn)步。其次,這是最后一個(gè)支持和的版本了,在后續(xù)的版本了會(huì)移除對(duì)它們的兼容。
譯者說
Tornado 4.3于2015年11月6日發(fā)布,該版本正式支持Python3.5的async/await關(guān)鍵字,并且用舊版本CPython編譯Tornado同樣可以使用這兩個(gè)關(guān)鍵字,這無疑是一種進(jìn)步。其次,這是最后一個(gè)支持Python2.6和Python3.2的版本了,在后續(xù)的版本了會(huì)移除對(duì)它們的兼容?,F(xiàn)在網(wǎng)絡(luò)上還沒有Tornado4.3的中文文檔,所以為了讓更多的朋友能接觸并學(xué)習(xí)到它,我開始了這個(gè)翻譯項(xiàng)目,希望感興趣的小伙伴可以一起參與翻譯,項(xiàng)目地址是tornado-zh on Github,翻譯好的文檔在Read the Docs上直接可以看到。歡迎Issues or PR。
Tornado web應(yīng)用的結(jié)構(gòu)通常一個(gè)Tornado web應(yīng)用包括一個(gè)或者多個(gè)RequestHandler 子類,一個(gè)可以將收到的請求路由到對(duì)應(yīng)handler的Application 對(duì)象,和一個(gè)啟動(dòng)服務(wù)的 main() 函數(shù).
一個(gè)最小的"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 對(duì)象
Application對(duì)象是負(fù)責(zé)全局配置的,包括映射請求轉(zhuǎn)發(fā)給處理程序的路由表.
路由表是URLSpec對(duì)象(或元組)的列表, 其中每個(gè)都包含(至少)一個(gè)正則表達(dá)式和一個(gè)處理類. 順序問題; 第一個(gè)匹配的規(guī)則會(huì)被使用. 如果正則表達(dá)式包含捕獲組, 這些組會(huì)被作為 路徑參數(shù) 傳遞給處理函數(shù)的HTTP方法.如果一個(gè)字典作為 URLSpec 的第三個(gè)參數(shù)被傳遞, 它會(huì)作為 初始參數(shù)傳遞給 RequestHandler.initialize. 最后 URLSpec 可能有一個(gè)名字(name), 這將允許它被 RequestHandler.reverse_url 使用.
例如, 在這個(gè)片段中根URL / 映射到了MainHandler , 像 /story/ 后跟著一個(gè)數(shù)字這種形式的URL被映射到了StoryHandler. 這個(gè)數(shù)字被傳遞(作為字符串)給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 構(gòu)造函數(shù)有很多關(guān)鍵字參數(shù)可以用于自定義應(yīng)用程序的行為和使用某些特性(或者功能); 完整列表請查看Application.settings .
RequestHandler 子類Tornado web 應(yīng)用程序的大部分工作是在RequestHandler子類下完成的.處理子類的主入口點(diǎn)是一個(gè)命名為處理HTTP方法的函數(shù): get(),post(), 等等. 每個(gè)處理程序可以定義一個(gè)或者多個(gè)這種方法來處理不同的HTTP動(dòng)作. 如上所述, 這些方法將被匹配路由規(guī)則的捕獲組對(duì)應(yīng)的參數(shù)調(diào)用.
在處理程序中, 調(diào)用方法如RequestHandler.render 或者RequestHandler.write 產(chǎn)生一個(gè)響應(yīng). render() 通過名字加載一個(gè)Template 并使用給定的參數(shù)渲染它. write() 被用于非模板基礎(chǔ)的輸出; 它接受字符串, 字節(jié), 和字典(字典會(huì)被編碼成JSON).
在RequestHandler 中的很多方法的設(shè)計(jì)是為了在子類中復(fù)寫和在整個(gè)應(yīng)用中使用. 常用的方法是定義一個(gè) BaseHandler 類, 復(fù)寫一些方法例如RequestHandler.write_error 和RequestHandler.get_current_user然后子類繼承使用你自己的 BaseHandler 而不是RequestHandler在你所有具體的處理程序中.
處理輸入請求處理請求的程序(request handler)可以使用 self.request 訪問代表當(dāng)前請求的對(duì)象. 通過tornado.httputil.HTTPServerRequest 的類定義查看完整的屬性列表.
使用HTML表單格式請求的數(shù)據(jù)會(huì)被解析并且可以在一些方法中使用, 例如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表單編碼不確定一個(gè)標(biāo)簽的參數(shù)是單一值還是一個(gè)列表,RequestHandler 有明確的方法來允許應(yīng)用程序表明是否它期望接收一個(gè)列表.對(duì)于列表, 使用RequestHandler.get_query_arguments 和RequestHandler.get_body_arguments 而不是它們的單數(shù)形式.
通過一個(gè)表單上傳的文件可以使用 self.request.files,它遍歷名字(HTML 標(biāo)簽 的name)到一個(gè)文件列表.每個(gè)文件都是一個(gè)字典的形式{"filename":..., "content_type":..., "body":...}. files對(duì)象是當(dāng)前唯一的如果文件上傳是通過一個(gè)表單包裝(i.e. a multipart/form-data Content-Type); 如果沒用這種格式,原生上傳的數(shù)據(jù)可以調(diào)用 self.request.body 使用.默認(rèn)上傳的文件是完全緩存在內(nèi)存中的; 如果你需要處理占用內(nèi)存太大的文件可以看看 stream_request_body 類裝飾器.
由于HTML表單編碼格式的怪異 (e.g. 在單數(shù)和復(fù)數(shù)參數(shù)的含糊不清), Tornado不會(huì)試圖統(tǒng)一表單參數(shù)和其他輸入類型的參數(shù). 特別是, 我們不解析JSON請求體.應(yīng)用程序希望使用JSON代替表單編碼可以復(fù)寫 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復(fù)寫RequestHandler的方法
除了 get()/post()/等, 在 .RequestHandler 中的某些其他方法被設(shè)計(jì)成了在必要的時(shí)候讓子類重寫. 在每個(gè)請求中, 會(huì)發(fā)生下面的調(diào)用序列:
在每次請求時(shí)生成一個(gè)新的 RequestHandler 對(duì)象
RequestHandler.initialize() 被 Application 配置中的初始化參數(shù)被調(diào)用. initialize 通常應(yīng)該只保存成員變量傳遞的參數(shù); 它不可能產(chǎn)生任何輸出或者調(diào)用方法, 例如RequestHandler.send_error.
RequestHandler.prepare() 被調(diào)用. 這在你所有處理子類共享的基類中是最有用的, 無論是使用哪種HTTP方法, prepare 都會(huì)被調(diào)用.prepare 可能會(huì)產(chǎn)生輸出; 如果它調(diào)用 RequestHandler.finish(或者 redirect, 等), 處理會(huì)在這里結(jié)束.
其中一種HTTP方法被調(diào)用: get(), post(), put(),等. 如果URL的正則表達(dá)式包含捕獲組, 它們會(huì)被作為參數(shù)傳遞給這個(gè)方法.
當(dāng)請求結(jié)束, RequestHandler.on_finish() 方法被調(diào)用. 對(duì)于同步處理程序會(huì)在 get() (等)后立即返回; 對(duì)于異步處理程序,會(huì)在調(diào)用RequestHandler.finish() 后返回.
所有這樣設(shè)計(jì)被用來復(fù)寫的方法被記錄在了RequestHandler的文檔中.其中最常用的一些被復(fù)寫的方法包括:
RequestHandler.write_error - 輸出對(duì)錯(cuò)誤頁面使用的HTML.
RequestHandler.on_connection_close - 當(dāng)客戶端斷開時(shí)被調(diào)用;應(yīng)用程序可以檢測這種情況,并中斷后續(xù)處理. 注意這不能保證一個(gè)關(guān)閉的連接及時(shí)被發(fā)現(xiàn).
RequestHandler.get_current_user - 參考 user-authentication
RequestHandler.get_user_locale - 返回 .Locale 對(duì)象給當(dāng)前
用戶使用
RequestHandler.set_default_headers - 可以被用來設(shè)置額外的響應(yīng)
頭(例如自定義的 Server 頭)
如果一個(gè)處理程序拋出一個(gè)異常, Tornado會(huì)調(diào)用RequestHandler.write_error 來生成一個(gè)錯(cuò)誤頁.tornado.web.HTTPError 可以被用來生成一個(gè)指定的狀態(tài)碼; 所有其他的異常都會(huì)返回一個(gè)500狀態(tài).
默認(rèn)的錯(cuò)誤頁面包含一個(gè)debug模式下的調(diào)用棧和另外一行錯(cuò)誤描述(e.g. "500: Internal Server Error"). 為了創(chuàng)建自定義的錯(cuò)誤頁面, 復(fù)寫RequestHandler.write_error (可能在一個(gè)所有處理程序共享的一個(gè)基類里面).這個(gè)方法可能產(chǎn)生輸出通常通過一些方法, 例如 RequestHandler.write 和RequestHandler.render. 如果錯(cuò)誤是由異常引起的, 一個(gè) exc_info 將作為一個(gè)關(guān)鍵字參數(shù)傳遞(注意這個(gè)異常不能保證是 sys.exc_info 當(dāng)前的異常, 所以 write_error 必須使用 e.g. traceback.format_exception 代替traceback.format_exc).
也可以在常規(guī)的處理方法中調(diào)用 RequestHandler.set_status 代替write_error 返回一個(gè)(自定義)響應(yīng)來生成一個(gè)錯(cuò)誤頁面. 特殊的例外tornado.web.Finish 在直接返回不方便的情況下能夠在不調(diào)用 write_error前結(jié)束處理程序.
對(duì)于404錯(cuò)誤, 使用 default_handler_class Application setting. 這個(gè)處理程序會(huì)復(fù)寫RequestHandler.prepare 而不是一個(gè)更具體的方法, 例如 get()所以它可以在任何HTTP方法下工作. 它應(yīng)該會(huì)產(chǎn)生如上所說的錯(cuò)誤頁面: 要么raise一個(gè) HTTPError(404) 要么復(fù)寫 write_error, 或者調(diào)用self.set_status(404) 或者在 prepare() 中直接生成響應(yīng).
重定向這里有兩種主要的方式讓你可以在Tornado中重定向請求:RequestHandler.redirect 和使用 RedirectHandler.
你可以在一個(gè) RequestHandler 的方法中使用 self.redirect() 把用戶重定向到其他地方. 還有一個(gè)可選參數(shù) permanent 你可以使用它來表明這個(gè)重定向被認(rèn)為是永久的. permanent 的默認(rèn)值是 False, 這會(huì)生成一個(gè)302 Found HTTP響應(yīng)狀態(tài)碼, 適合類似在用戶的 POST 請求成功后的重定向.如果 permanent 是true, 會(huì)使用 301 Moved Permanently HTTP響應(yīng), 更適合e.g. 在SEO友好的方法中把一個(gè)頁面重定向到一個(gè)權(quán)威的URL.
RedirectHandler 讓你直接在你 Application 路由表中配置. 例如, 配置一個(gè)靜態(tài)重定向:
app = tornado.web.Application([ url(r"/app", tornado.web.RedirectHandler, dict(url="http://itunes.apple.com/my-app-id")), ])
RedirectHandler 也支持正則表達(dá)式替換. 下面的規(guī)則重定向所有以 /pictures/開始的請求用 /photos/ 前綴代替:
app = tornado.web.Application([ url(r"/photos/(.*)", MyPhotoHandler), url(r"/pictures/(.*)", tornado.web.RedirectHandler, dict(url=r"/photos/1")), ])
不像 RequestHandler.redirect, RedirectHandler 默認(rèn)使用永久重定向.這是因?yàn)槁酚杀碓谶\(yùn)行時(shí)不會(huì)改變, 而且被認(rèn)為是永久的.當(dāng)在處理程序中發(fā)現(xiàn)重定向的時(shí)候, 可能是其他可能改變的邏輯的結(jié)果.用 .RedirectHandler 發(fā)送臨時(shí)重定向, 需要添加 permanent=False 到.RedirectHandler 的初始化參數(shù).
異步處理Tornado默認(rèn)會(huì)同步處理: 當(dāng) get()/post() 方法返回, 請求被認(rèn)為結(jié)束并且返回響應(yīng). 因?yàn)楫?dāng)一個(gè)處理程序正在運(yùn)行的時(shí)候其他所有請求都被阻塞,任何需要長時(shí)間運(yùn)行的處理都應(yīng)該是異步的, 這樣它就可以在非阻塞的方式中調(diào)用它的慢操作了. 這個(gè)話題更詳細(xì)的內(nèi)容包含在async 中; 這部分是關(guān)于在 RequestHandler 子類中的異步技術(shù)的細(xì)節(jié).
使用 coroutine 裝飾器是做異步最簡單的方式. 這允許你使用 yield 關(guān)鍵字執(zhí)行非阻塞I/O, 并且直到協(xié)程返回才發(fā)送響應(yīng). 查看 coroutines了解更多細(xì)節(jié).
在某些情況下, 協(xié)程不如回調(diào)為主的風(fēng)格方便, 在這種情況下tornado.web.asynchronous 裝飾器可以用來代替. 當(dāng)使用這個(gè)裝飾器的時(shí)候,響應(yīng)不會(huì)自動(dòng)發(fā)送; 而請求將一直保持開放直到callback調(diào)用RequestHandler.finish. 這需要應(yīng)用程序確保這個(gè)方法被調(diào)用或者其他用戶的瀏覽器簡單的掛起.
這里是一個(gè)使用Tornado"s 內(nèi)置的 AsyncHTTPClient 調(diào)用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()
當(dāng) get() 返回, 請求還沒有完成. 當(dāng)HTTP客戶端最終調(diào)用on_response(), 這個(gè)請求仍然是開放的, 響應(yīng)最終刷到客戶端通過調(diào)用 self.finish().
為了方便對(duì)比, 這里有一個(gè)使用協(xié)程的相同的例子:
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")
更多高級(jí)異步的示例, 請看chat example application, 實(shí)現(xiàn)了一個(gè)使用 長輪詢(long polling)的AJAX聊天室.使用長輪詢的用戶可能想要覆蓋 on_connection_close() 來在客戶端關(guān)閉連接之后進(jìn)行清理(注意看方法的文檔來查看警告).
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/37705.html
摘要:譯者說于年月日發(fā)布,該版本正式支持的關(guān)鍵字,并且用舊版本編譯同樣可以使用這兩個(gè)關(guān)鍵字,這無疑是一種進(jìn)步。其次,這是最后一個(gè)支持和的版本了,在后續(xù)的版本了會(huì)移除對(duì)它們的兼容。 譯者說 Tornado 4.3于2015年11月6日發(fā)布,該版本正式支持Python3.5的async/await關(guān)鍵字,并且用舊版本CPython編譯Tornado同樣可以使用這兩個(gè)關(guān)鍵字,這無疑是一種進(jìn)步。其次...
摘要:譯者說于年月日發(fā)布,該版本正式支持的關(guān)鍵字,并且用舊版本編譯同樣可以使用這兩個(gè)關(guān)鍵字,這無疑是一種進(jìn)步。其次,這是最后一個(gè)支持和的版本了,在后續(xù)的版本了會(huì)移除對(duì)它們的兼容。 譯者說 Tornado 4.3于2015年11月6日發(fā)布,該版本正式支持Python3.5的async/await關(guān)鍵字,并且用舊版本CPython編譯Tornado同樣可以使用這兩個(gè)關(guān)鍵字,這無疑是一種進(jìn)步。其次...
閱讀 3747·2021-11-24 09:39
閱讀 3488·2019-08-30 15:56
閱讀 1382·2019-08-30 15:55
閱讀 1043·2019-08-30 15:53
閱讀 1932·2019-08-29 18:37
閱讀 3614·2019-08-29 18:32
閱讀 3142·2019-08-29 16:30
閱讀 2947·2019-08-29 15:14