本書(shū)出處:http://demo.pythoner.com/itt2zh/
中文翻譯:你像從前一樣
在過(guò)去的五年里,Web開(kāi)發(fā)人員的可用工具實(shí)現(xiàn)了跨越式地增長(zhǎng)。當(dāng)技術(shù)專家不斷推動(dòng)極限,使Web應(yīng)用無(wú)處不在時(shí),我們也不得不升級(jí)我們的工具、創(chuàng)建框架以保證構(gòu)建更好的應(yīng)用。我們希望能夠使用新的工具,方便我們寫(xiě)出更加整潔、可維護(hù)的代碼,使部署到世界各地的用戶時(shí)擁有高效的可擴(kuò)展性。
這就讓我們談?wù)摰絋ornado,一個(gè)編寫(xiě)易創(chuàng)建、擴(kuò)展和部署的強(qiáng)力Web應(yīng)用的夢(mèng)幻選擇。我們?nèi)齻€(gè)都因?yàn)門(mén)ornado的速度、簡(jiǎn)單和可擴(kuò)展性而深深地愛(ài)上了它,在一些個(gè)人項(xiàng)目中嘗試之后,我們將其運(yùn)用到日常工作中。我們已經(jīng)看到,Tornado在很多大型或小型的項(xiàng)目中提升了開(kāi)發(fā)者的速度(和樂(lè)趣?。?,同時(shí),其魯棒性和輕量級(jí)也給開(kāi)發(fā)者一次又一次留下了深刻的印象。
本書(shū)的目的是對(duì)Tornado Web服務(wù)器進(jìn)行一個(gè)概述,通過(guò)框架基礎(chǔ)、一些示例應(yīng)用和真實(shí)世界使用的最佳實(shí)踐來(lái)引導(dǎo)讀者。我們將使用示例來(lái)詳細(xì)講解Tornado如何工作,你可以用它做什么,以及在構(gòu)建自己第一個(gè)應(yīng)用時(shí)要避免什么。
在本書(shū)中,我們假定你對(duì)Python已經(jīng)有了粗略的了解,知道Web服務(wù)如何運(yùn)作,對(duì)數(shù)據(jù)庫(kù)有一定的熟悉。有一些不錯(cuò)的書(shū)籍可以為你深入了解這些提供參考(比如Learning Python,Restful Web Service和MongoDB: The Definitive Guide)。
你可以在Github上獲得本書(shū)中示例的代碼。如果你有關(guān)于這些示例或其他方面的任何思想,歡迎在那里告訴我們。
所以,事不宜遲,讓我們開(kāi)始深入了解吧!
Tornado是使用Python編寫(xiě)的一個(gè)強(qiáng)大的、可擴(kuò)展的Web服務(wù)器。它在處理嚴(yán)峻的網(wǎng)絡(luò)流量時(shí)表現(xiàn)得足夠強(qiáng)健,但卻在創(chuàng)建和編寫(xiě)時(shí)有著足夠的輕量級(jí),并能夠被用在大量的應(yīng)用和工具中。
我們現(xiàn)在所知道的Tornado是基于Bret Taylor和其他人員為FriendFeed所開(kāi)發(fā)的網(wǎng)絡(luò)服務(wù)框架,當(dāng)FriendFeed被Facebook收購(gòu)后得以開(kāi)源。不同于那些最多只能達(dá)到10,000個(gè)并發(fā)連接的傳統(tǒng)網(wǎng)絡(luò)服務(wù)器,Tornado在設(shè)計(jì)之初就考慮到了性能因素,旨在解決C10K問(wèn)題,這樣的設(shè)計(jì)使得其成為一個(gè)擁有非常高性能的框架。此外,它還擁有處理安全性、用戶驗(yàn)證、社交網(wǎng)絡(luò)以及與外部服務(wù)(如數(shù)據(jù)庫(kù)和網(wǎng)站API)進(jìn)行異步交互的工具。
延伸閱讀:C10K問(wèn)題
基于線程的服務(wù)器,如Apache,為了傳入的連接,維護(hù)了一個(gè)操作系統(tǒng)的線程池。Apache會(huì)為每個(gè)HTTP連接分配線程池中的一個(gè)線程,如果所有的線程都處于被占用的狀態(tài)并且尚有內(nèi)存可用時(shí),則生成一個(gè)新的線程。盡管不同的操作系統(tǒng)會(huì)有不同的設(shè)置,大多數(shù)Linux發(fā)布版中都是默認(rèn)線程堆大小為8MB。Apache的架構(gòu)在大負(fù)載下變得不可預(yù)測(cè),為每個(gè)打開(kāi)的連接維護(hù)一個(gè)大的線程池等待數(shù)據(jù)極易迅速耗光服務(wù)器的內(nèi)存資源。
大多數(shù)社交網(wǎng)絡(luò)應(yīng)用都會(huì)展示實(shí)時(shí)更新來(lái)提醒新消息、狀態(tài)變化以及用戶通知,這就要求客戶端需要保持一個(gè)打開(kāi)的連接來(lái)等待服務(wù)器端的任何響應(yīng)。這些長(zhǎng)連接或推送請(qǐng)求使得Apache的最大線程池迅速飽和。一旦線程池的資源耗盡,服務(wù)器將不能再響應(yīng)新的請(qǐng)求。
異步服務(wù)器在這一場(chǎng)景中的應(yīng)用相對(duì)較新,但他們正是被設(shè)計(jì)用來(lái)減輕基于線程的服務(wù)器的限制的。當(dāng)負(fù)載增加時(shí),諸如Node.js,lighttpd和Tornodo這樣的服務(wù)器使用協(xié)作的多任務(wù)的方式進(jìn)行優(yōu)雅的擴(kuò)展。也就是說(shuō),如果當(dāng)前請(qǐng)求正在等待來(lái)自其他資源的數(shù)據(jù)(比如數(shù)據(jù)庫(kù)查詢或HTTP請(qǐng)求)時(shí),一個(gè)異步服務(wù)器可以明確地控制以掛起請(qǐng)求。異步服務(wù)器用來(lái)恢復(fù)暫停的操作的一個(gè)常見(jiàn)模式是當(dāng)合適的數(shù)據(jù)準(zhǔn)備好時(shí)調(diào)用回調(diào)函數(shù)。我們將會(huì)在第五章講解回調(diào)函數(shù)模式以及一系列Tornado異步功能的應(yīng)用。
自從2009年9月10日發(fā)布以來(lái),TornadoTornado已經(jīng)獲得了很多社區(qū)的支持,并且在一系列不同的場(chǎng)合得到應(yīng)用。除FriendFeed和Facebook外,還有很多公司在生產(chǎn)上轉(zhuǎn)向Tornado,包括Quora、Turntable.fm、Bit.ly、Hipmunk以及MyYearbook等。
總之,如果你在尋找你那龐大的CMS或一體化開(kāi)發(fā)框架的替代品,Tornado可能并不是一個(gè)好的選擇。Tornado并不需要你擁有龐大的模型建立特殊的方式,或以某種確定的形式處理表單,或其他類似的事情。它所做的是讓你能夠快速簡(jiǎn)單地編寫(xiě)高速的Web應(yīng)用。如果你想編寫(xiě)一個(gè)可擴(kuò)展的社交應(yīng)用、實(shí)時(shí)分析引擎,或RESTful API,那么簡(jiǎn)單而強(qiáng)大的Python,以及Tornado(和這本書(shū))正是為你準(zhǔn)備的!
在大部分*nix系統(tǒng)中安裝Tornado非常容易--你既可以從PyPI獲?。ú⑹褂?code>easy_install或pip
安裝),也可以從Github上下載源碼編譯安裝,如下所示[1]:
$ curl -L -O https://github.com/facebook/tornado/archive/v3.1.0.tar.gz
$ tar xvzf v3.1.0.tar.gz
$ cd tornado-3.1.0
$ python setup.py build
$ sudo python setup.py install
Tornado官方并不支持Windows,但你可以通過(guò)ActivePython的PyPM包管理器進(jìn)行安裝,類似如下所示:
C:\> pypm install tornado
一旦Tornado在你的機(jī)器上安裝好,你就可以很好的開(kāi)始了!壓縮包中包含很多demo,比如建立博客、整合Facebook、運(yùn)行聊天服務(wù)等的示例代碼。我們稍后會(huì)在本書(shū)中通過(guò)一些示例應(yīng)用逐步講解,不過(guò)你也應(yīng)該看看這些官方demo。
本書(shū)中的代碼假定你使用的是基于Unix的系統(tǒng),并且使用的是Python2.6或2.7版本。如果是這樣,你就不需要任何除了Python標(biāo)準(zhǔn)庫(kù)之外的東西。如果你的Python版本是2.5或更低,在安裝pycURL、simpleJSON和Python開(kāi)發(fā)頭文件后可以運(yùn)行Tornado。[2]
對(duì)于問(wèn)題、示例和一般的指南,Tornado官方文檔是個(gè)不錯(cuò)的選擇。在tornadoweb.org上有大量的例子和功能缺陷,更多細(xì)節(jié)和變更可以在Tornado在Github上的版本庫(kù)中看到。而對(duì)于更具體的問(wèn)題,可以到Tornado的Google Group中咨詢,那里有很多活躍的日常使用Tornado的開(kāi)發(fā)者。
既然我們已經(jīng)知道了Tornado是什么了,現(xiàn)在讓我們看看它能做什么吧。我們首先從使用Tornado編寫(xiě)一個(gè)簡(jiǎn)單的Web應(yīng)用開(kāi)始。
Tornado是一個(gè)編寫(xiě)對(duì)HTTP請(qǐng)求響應(yīng)的框架。作為程序員,你的工作是編寫(xiě)響應(yīng)特定條件HTTP請(qǐng)求的響應(yīng)的handler。下面是一個(gè)全功能的Tornado應(yīng)用的基礎(chǔ)示例:
代碼清單1-1 基礎(chǔ):hello.py
import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado.web
from tornado.options import define, options
define("port", default=8000, help="run on the given port", type=int)
class IndexHandler(tornado.web.RequestHandler):
def get(self):
greeting = self.get_argument('greeting', 'Hello')
self.write(greeting + ', friendly user!')
if __name__ == "__main__":
tornado.options.parse_command_line()
app = tornado.web.Application(handlers=[(r"/", IndexHandler)])
http_server = tornado.httpserver.HTTPServer(app)
http_server.listen(options.port)
tornado.ioloop.IOLoop.instance().start()
編寫(xiě)一個(gè)Tornado應(yīng)用中最多的工作是定義類繼承Tornado的RequestHandler類。在這個(gè)例子中,我們創(chuàng)建了一個(gè)簡(jiǎn)單的應(yīng)用,在給定的端口監(jiān)聽(tīng)請(qǐng)求,并在根目錄("/")響應(yīng)請(qǐng)求。
你可以在命令行里嘗試運(yùn)行這個(gè)程序以測(cè)試輸出:
$ python hello.py --port=8000
現(xiàn)在你可以在瀏覽器中打開(kāi)http://localhost:8000,或者打開(kāi)另一個(gè)終端窗口使用curl測(cè)試我們的應(yīng)用:
$ curl http://localhost:8000/
Hello, friendly user!
$ curl http://localhost:8000/?greeting=Salutations
Salutations, friendly user!
讓我們把這個(gè)例子分成小塊,逐步分析它們:
import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado.web
在程序的最頂部,我們導(dǎo)入了一些Tornado模塊。雖然Tornado還有另外一些有用的模塊,但在這個(gè)例子中我們必須至少包含這四個(gè)模塊。
from tornado.options import define, options
define("port", default=8000, help="run on the given port", type=int)
Tornado包括了一個(gè)有用的模塊(tornado.options)來(lái)從命令行中讀取設(shè)置。我們?cè)谶@里使用這個(gè)模塊指定我們的應(yīng)用監(jiān)聽(tīng)HTTP請(qǐng)求的端口。它的工作流程如下:如果一個(gè)與define語(yǔ)句中同名的設(shè)置在命令行中被給出,那么它將成為全局options的一個(gè)屬性。如果用戶運(yùn)行程序時(shí)使用了--help
選項(xiàng),程序?qū)⒋蛴〕鏊心愣x的選項(xiàng)以及你在define函數(shù)的help參數(shù)中指定的文本。如果用戶沒(méi)有為這個(gè)選項(xiàng)指定值,則使用default的值進(jìn)行代替。Tornado使用type參數(shù)進(jìn)行基本的參數(shù)類型驗(yàn)證,當(dāng)不合適的類型被給出時(shí)拋出一個(gè)異常。因此,我們?cè)试S一個(gè)整數(shù)的port參數(shù)作為options.port來(lái)訪問(wèn)程序。如果用戶沒(méi)有指定值,則默認(rèn)為8000。
class IndexHandler(tornado.web.RequestHandler):
def get(self):
greeting = self.get_argument('greeting', 'Hello')
self.write(greeting + ', friendly user!')
這是Tornado的請(qǐng)求處理函數(shù)類。當(dāng)處理一個(gè)請(qǐng)求時(shí),Tornado將這個(gè)類實(shí)例化,并調(diào)用與HTTP請(qǐng)求方法所對(duì)應(yīng)的方法。在這個(gè)例子中,我們只定義了一個(gè)get方法,也就是說(shuō)這個(gè)處理函數(shù)將對(duì)HTTP的GET請(qǐng)求作出響應(yīng)。我們稍后將看到實(shí)現(xiàn)不止一個(gè)HTTP方法的處理函數(shù)。
greeting = self.get_argument('greeting', 'Hello')
Tornado的RequestHandler類有一系列有用的內(nèi)建方法,包括get_argument,我們?cè)谶@里從一個(gè)查詢字符串中取得參數(shù)greeting的值。(如果這個(gè)參數(shù)沒(méi)有出現(xiàn)在查詢字符串中,Tornado將使用get_argument的第二個(gè)參數(shù)作為默認(rèn)值。)
self.write(greeting + ', friendly user!')
RequestHandler的另一個(gè)有用的方法是write,它以一個(gè)字符串作為函數(shù)的參數(shù),并將其寫(xiě)入到HTTP響應(yīng)中。在這里,我們使用請(qǐng)求中g(shù)reeting參數(shù)提供的值插入到greeting中,并寫(xiě)回到響應(yīng)中。
if __name__ == "__main__":
tornado.options.parse_command_line()
app = tornado.web.Application(handlers=[(r"/", IndexHandler)])
這是真正使得Tornado運(yùn)轉(zhuǎn)起來(lái)的語(yǔ)句。首先,我們使用Tornado的options模塊來(lái)解析命令行。然后我們創(chuàng)建了一個(gè)Tornado的Application類的實(shí)例。傳遞給Application類init方法的最重要的參數(shù)是handlers。它告訴Tornado應(yīng)該用哪個(gè)類來(lái)響應(yīng)請(qǐng)求。馬上我們講解更多相關(guān)知識(shí)。
http_server = tornado.httpserver.HTTPServer(app)
http_server.listen(options.port)
tornado.ioloop.IOLoop.instance().start()
從這里開(kāi)始的代碼將會(huì)被反復(fù)使用:一旦Application對(duì)象被創(chuàng)建,我們可以將其傳遞給Tornado的HTTPServer對(duì)象,然后使用我們?cè)诿钚兄付ǖ亩丝谶M(jìn)行監(jiān)聽(tīng)(通過(guò)options對(duì)象取出。)最后,在程序準(zhǔn)備好接收HTTP請(qǐng)求后,我們創(chuàng)建一個(gè)Tornado的IOLoop的實(shí)例。
讓我們?cè)倏匆谎踙ello.py示例中的這一行:
app = tornado.web.Application(handlers=[(r"/", IndexHandler)])
這里的參數(shù)handlers非常重要,值得我們更加深入的研究。它應(yīng)該是一個(gè)元組組成的列表,其中每個(gè)元組的第一個(gè)元素是一個(gè)用于匹配的正則表達(dá)式,第二個(gè)元素是一個(gè)RequestHanlder類。在hello.py中,我們只指定了一個(gè)正則表達(dá)式-RequestHanlder對(duì),但你可以按你的需要指定任意多個(gè)。
Tornado在元組中使用正則表達(dá)式來(lái)匹配HTTP請(qǐng)求的路徑。(這個(gè)路徑是URL中主機(jī)名后面的部分,不包括查詢字符串和碎片。)Tornado把這些正則表達(dá)式看作已經(jīng)包含了行開(kāi)始和結(jié)束錨點(diǎn)(即,字符串"/"被看作為"^/$")。
如果一個(gè)正則表達(dá)式包含一個(gè)捕獲分組(即,正則表達(dá)式中的部分被括號(hào)括起來(lái)),匹配的內(nèi)容將作為相應(yīng)HTTP請(qǐng)求的參數(shù)傳到RequestHandler對(duì)象中。我們將在下個(gè)例子中看到它的用法。
例1-2是一個(gè)我們目前為止看到的更復(fù)雜的例子,它將介紹更多Tornado的基本概念。
代碼清單1-2 處理輸入:string_service.py
import textwrap
import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado.web
from tornado.options import define, options
define("port", default=8000, help="run on the given port", type=int)
class ReverseHandler(tornado.web.RequestHandler):
def get(self, input):
self.write(input[::-1])
class WrapHandler(tornado.web.RequestHandler):
def post(self):
text = self.get_argument('text')
width = self.get_argument('width', 40)
self.write(textwrap.fill(text, int(width)))
if __name__ == "__main__":
tornado.options.parse_command_line()
app = tornado.web.Application(
handlers=[
(r"/reverse/(\w+)", ReverseHandler),
(r"/wrap", WrapHandler)
]
)
http_server = tornado.httpserver.HTTPServer(app)
http_server.listen(options.port)
tornado.ioloop.IOLoop.instance().start()
如同運(yùn)行第一個(gè)例子,你可以在命令行中運(yùn)行這個(gè)例子使用如下的命令:
$ python string_service.py --port=8000
這個(gè)程序是一個(gè)通用的字符串操作的Web服務(wù)端基本框架。到目前為止,你可以用它做兩件事情。其一,到/reverse/string
的GET請(qǐng)求將會(huì)返回URL路徑中指定字符串的反轉(zhuǎn)形式。
$ curl http://localhost:8000/reverse/stressed
desserts
$ curl http://localhost:8000/reverse/slipup
pupils
其二,到/wrap
的POST請(qǐng)求將從參數(shù)text中取得指定的文本,并返回按照參數(shù)width指定寬度裝飾的文本。下面的請(qǐng)求指定一個(gè)沒(méi)有寬度的字符串,所以它的輸出寬度被指定為程序中的get_argument的默認(rèn)值40個(gè)字符。
$ http://localhost:8000/wrap -d text=Lorem+ipsum+dolor+sit+amet,+consectetuer+adipiscing+elit.
Lorem ipsum dolor sit amet, consectetuer
adipiscing elit.
字符串服務(wù)示例和上一節(jié)示例代碼中大部分是一樣的。讓我們關(guān)注那些新的代碼。首先,讓我們看看傳遞給Application構(gòu)造函數(shù)的handlers參數(shù)的值:
app = tornado.web.Application(handlers=[
(r"/reverse/(\w+)", ReverseHandler),
(r"/wrap", WrapHandler)
])
在上面的代碼中,Application類在"handlers"參數(shù)中實(shí)例化了兩個(gè)RequestHandler類對(duì)象。第一個(gè)引導(dǎo)Tornado傳遞路徑匹配下面的正則表達(dá)式的請(qǐng)求:
/reverse/(\w+)
正則表達(dá)式告訴Tornado匹配任何以字符串/reverse/開(kāi)始并緊跟著一個(gè)或多個(gè)字母的路徑。括號(hào)的含義是讓Tornado保存匹配括號(hào)里面表達(dá)式的字符串,并將其作為請(qǐng)求方法的一個(gè)參數(shù)傳遞給RequestHandler類。讓我們檢查ReverseHandler的定義來(lái)看看它是如何工作的:
class ReverseHandler(tornado.web.RequestHandler):
def get(self, input):
self.write(input[::-1])
你可以看到這里的get方法有一個(gè)額外的參數(shù)input。這個(gè)參數(shù)將包含匹配處理函數(shù)正則表達(dá)式第一個(gè)括號(hào)里的字符串。(如果正則表達(dá)式中有一系列額外的括號(hào),匹配的字符串將被按照在正則表達(dá)式中出現(xiàn)的順序作為額外的參數(shù)傳遞進(jìn)來(lái)。)
現(xiàn)在,讓我們看一下WrapHandler的定義:
class WrapHandler(tornado.web.RequestHandler):
def post(self):
text = self.get_argument('text')
width = self.get_argument('width', 40)
self.write(textwrap.fill(text, int(width)))
WrapHandler類處理匹配路徑為/wrap
的請(qǐng)求。這個(gè)處理函數(shù)定義了一個(gè)post方法,也就是說(shuō)它接收HTTP的POST方法的請(qǐng)求。
我們之前使用RequestHandler對(duì)象的get_argument方法來(lái)捕獲請(qǐng)求查詢字符串的的參數(shù)。同樣,我們也可以使用相同的方法來(lái)獲得POST請(qǐng)求傳遞的參數(shù)。(Tornado可以解析URLencoded和multipart結(jié)構(gòu)的POST請(qǐng)求)。一旦我們從POST中獲得了文本和寬度的參數(shù),我們使用Python內(nèi)建的textwrap模塊來(lái)以指定的寬度裝飾文本,并將結(jié)果字符串寫(xiě)回到HTTP響應(yīng)中。
到目前為止,我們已經(jīng)了解了RequestHandler對(duì)象的基礎(chǔ):如何從一個(gè)傳入的HTTP請(qǐng)求中獲得信息(使用get_argument和傳入到get和post的參數(shù))以及寫(xiě)HTTP響應(yīng)(使用write方法)。除此之外,還有很多需要學(xué)習(xí)的,我們將在接下來(lái)的章節(jié)中進(jìn)行講解。同時(shí),還有一些關(guān)于RequestHandler和Tornado如何使用它的只是需要記住。
截止到目前討論的例子,每個(gè)RequestHandler類都只定義了一個(gè)HTTP方法的行為。但是,在同一個(gè)處理函數(shù)中定義多個(gè)方法是可能的,并且是有用的。把概念相關(guān)的功能綁定到同一個(gè)類是一個(gè)很好的方法。比如,你可能會(huì)編寫(xiě)一個(gè)處理函數(shù)來(lái)處理數(shù)據(jù)庫(kù)中某個(gè)特定ID的對(duì)象,既使用GET方法,也使用POST方法。想象GET方法來(lái)返回這個(gè)部件的信息,而POST方法在數(shù)據(jù)庫(kù)中對(duì)這個(gè)ID的部件進(jìn)行改變:
# matched with (r"/widget/(\d+)", WidgetHandler)
class WidgetHandler(tornado.web.RequestHandler):
def get(self, widget_id):
widget = retrieve_from_db(widget_id)
self.write(widget.serialize())
def post(self, widget_id):
widget = retrieve_from_db(widget_id)
widget['foo'] = self.get_argument('foo')
save_to_db(widget)
我們到目前為止只是用了GET和POST方法,但Tornado支持任何合法的HTTP請(qǐng)求(GET、POST、PUT、DELETE、HEAD、OPTIONS)。你可以非常容易地定義上述任一種方法的行為,只需要在RequestHandler類中使用同名的方法。下面是另一個(gè)想象的例子,在這個(gè)例子中針對(duì)特定frob ID的HEAD請(qǐng)求只根據(jù)frob是否存在給出信息,而GET方法返回整個(gè)對(duì)象:
# matched with (r"/frob/(\d+)", FrobHandler)
class FrobHandler(tornado.web.RequestHandler):
def head(self, frob_id):
frob = retrieve_from_db(frob_id)
if frob is not None:
self.set_status(200)
else:
self.set_status(404)
def get(self, frob_id):
frob = retrieve_from_db(frob_id)
self.write(frob.serialize())
從上面的代碼可以看出,你可以使用RequestHandler類的ser_status()方法顯式地設(shè)置HTTP狀態(tài)碼。然而,你需要記住在某些情況下,Tornado會(huì)自動(dòng)地設(shè)置HTTP狀態(tài)碼。下面是一個(gè)常用情況的綱要:
Tornado會(huì)在HTTP請(qǐng)求的路徑無(wú)法匹配任何RequestHandler類相對(duì)應(yīng)的模式時(shí)返回404(Not Found)響應(yīng)碼。
如果你調(diào)用了一個(gè)沒(méi)有默認(rèn)值的get_argument函數(shù),并且沒(méi)有發(fā)現(xiàn)給定名稱的參數(shù),Tornado將自動(dòng)返回一個(gè)400(Bad Request)響應(yīng)碼。
如果傳入的請(qǐng)求使用了RequestHandler中沒(méi)有定義的HTTP方法(比如,一個(gè)POST請(qǐng)求,但是處理函數(shù)中只有定義了get方法),Tornado將返回一個(gè)405(Methos Not Allowed)響應(yīng)碼。
當(dāng)程序遇到任何不能讓其退出的錯(cuò)誤時(shí),Tornado將返回500(Internal Server Error)響應(yīng)碼。你代碼中任何沒(méi)有捕獲的異常也會(huì)導(dǎo)致500響應(yīng)碼。
如果響應(yīng)成功,并且沒(méi)有其他返回碼被設(shè)置,Tornado將默認(rèn)返回一個(gè)200(OK)響應(yīng)碼。
當(dāng)上述任何一種錯(cuò)誤發(fā)生時(shí),Tornado將默認(rèn)向客戶端發(fā)送一個(gè)包含狀態(tài)碼和錯(cuò)誤信息的簡(jiǎn)短片段。如果你想使用自己的方法代替默認(rèn)的錯(cuò)誤響應(yīng),你可以重寫(xiě)write_error方法在你的RequestHandler類中。比如,代碼清單1-3是hello.py示例添加了常規(guī)的錯(cuò)誤消息的版本。
代碼清單1-3 常規(guī)錯(cuò)誤響應(yīng):hello-errors.py
import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado.web
from tornado.options import define, options
define("port", default=8000, help="run on the given port", type=int)
class IndexHandler(tornado.web.RequestHandler):
def get(self):
greeting = self.get_argument('greeting', 'Hello')
self.write(greeting + ', friendly user!')
def write_error(self, status_code, **kwargs):
self.write("Gosh darnit, user! You caused a %d error." % status_code)
if __name__ == "__main__":
tornado.options.parse_command_line()
app = tornado.web.Application(handlers=[(r"/", IndexHandler)])
http_server = tornado.httpserver.HTTPServer(app)
http_server.listen(options.port)
tornado.ioloop.IOLoop.instance().start()
當(dāng)我們嘗試一個(gè)POST請(qǐng)求時(shí),會(huì)得到下面的響應(yīng)。一般來(lái)說(shuō),我們應(yīng)該得到Tornado默認(rèn)的錯(cuò)誤響應(yīng),但因?yàn)槲覀兏矊?xiě)了write_error,我們會(huì)得到不一樣的東西:
$ curl -d foo=bar http://localhost:8000/
Gosh darnit, user! You caused a 405 error.
現(xiàn)在你已經(jīng)明白了最基本的東西,我們渴望你想了解更多。在接下來(lái)的章節(jié),我們將向你展示能夠幫助你使用Tornado創(chuàng)建成熟的Web服務(wù)和應(yīng)用的功能和技術(shù)。首先是:Tornado的模板系統(tǒng)。
[1] 壓縮包地址已更新到Tornado的最新版本3.1.0。
[2] 書(shū)中原文中關(guān)于Python3.X版本的兼容性問(wèn)題目前已不存在,因此省略該部分。
更多建議: