Tornado Web應(yīng)用程序的結(jié)構(gòu)

2022-03-08 10:40 更新

Tornado Web 應(yīng)用程序通常由一個(gè)或多個(gè) RequestHandler子類(lèi),一個(gè)Application將傳入請(qǐng)求路由到處理程序的對(duì)象和一個(gè)?main()?啟動(dòng)服務(wù)器的函數(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é)全局配置,包括將請(qǐng)求映射到處理程序的路由表。

路由表是?URLSpec?對(duì)象(或元組)的列表,每個(gè)對(duì)象(至少)包含一個(gè)正則表達(dá)式和一個(gè)處理程序類(lèi)。訂單事項(xiàng);使用第一個(gè)匹配規(guī)則。如果正則表達(dá)式包含捕獲組,這些組是路徑參數(shù),并將傳遞給處理程序的 HTTP 方法。如果字典作為 的第三個(gè)元素傳遞URLSpec,它提供將傳遞給 的初始化參數(shù)RequestHandler.initialize。最后,URLSpec可能有一個(gè)名稱(chēng),這將允許它與 一起使用 RequestHandler.reverse_url。

例如,在這段代碼中,根URL?/?被映射到?MainHandler?,格式為?/story/?后跟數(shù)字的URL被映射到?StoryHandler?。該數(shù)字(作為字符串)傳遞給?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構(gòu)造函數(shù)接受許多關(guān)鍵字參數(shù),這些參數(shù)可用于自定義應(yīng)用程序的行為并啟用可選功能;查看Application.settings的完整列表。

RequestHandler子類(lèi)

Tornado Web 應(yīng)用程序的大部分工作都是在RequestHandler. 處理程序子類(lèi)的主要入口點(diǎn)是一個(gè)以正在處理的 HTTP 方法命名的方法:?get()?、 ?post()?等。每個(gè)處理程序可以定義一個(gè)或多個(gè)這些方法來(lái)處理不同的 HTTP 操作。如上所述,將使用與匹配的路由規(guī)則的捕獲組對(duì)應(yīng)的參數(shù)調(diào)用這些方法。

在處理程序中,調(diào)用RequestHandler.renderRequestHandler.write等方法以生成響應(yīng)。?render()?按名稱(chēng)加載Template,并用給定的參數(shù)呈現(xiàn)它。?write()?用于非基于模板的輸出;它接受字符串、字節(jié)和字典(dict將被編碼為JSON)。

RequestHandler中的許多方法被設(shè)計(jì)為在子類(lèi)中重寫(xiě),并在整個(gè)應(yīng)用程序中使用。通常會(huì)定義一個(gè)?BaseHandler?類(lèi)來(lái)覆蓋write_errorget_current_user等方法,然后為所有特定的處理程序子類(lèi)化自己的?BaseHandler?而不是RequestHandler。

處理請(qǐng)求輸入

請(qǐng)求處理程序可以通過(guò)?self.request?訪問(wèn)表示當(dāng)前請(qǐng)求的對(duì)象。有關(guān)屬性的完整列表,請(qǐng)參見(jiàn)HTTPServerRequest的類(lèi)定義。

HTML表單使用的格式的請(qǐng)求數(shù)據(jù)將被解析,并通過(guò)get_query_argumentget_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表單編碼對(duì)于參數(shù)是單個(gè)值還是包含一個(gè)元素的列表是不明確的,RequestHandler有不同的方法來(lái)允許應(yīng)用程序指示它是否需要列表。對(duì)于列表,請(qǐng)使用get_query_argumentsget_body_arguments,而不是它們的單數(shù)對(duì)應(yīng)項(xiàng)。

通過(guò)表單上傳的文件在?self.request.files?中可用,它將名稱(chēng)(HTML ?<input type="file">?元素的名稱(chēng))映射到文件列表。每個(gè)文件都是?{"filename":...,"content_type":...,"body":...}?格式的字典。?files?對(duì)象僅在使用表單包裝器(即?multipart/form-data?內(nèi)容類(lèi)型)上載文件時(shí)存在;如果未使用此格式,則?self.request.body?中可獲得原始上傳數(shù)據(jù)。默認(rèn)情況下,上傳的文件在內(nèi)存中完全緩沖;如果需要處理太大而無(wú)法輕松保存在內(nèi)存中的文件,請(qǐng)參閱stream_request_body類(lèi)裝飾器。

在demos目錄中,file_receiver.py顯示了接收文件上傳的兩種方法。

由于HTML表單編碼的特殊性(例如,單數(shù)和復(fù)數(shù)參數(shù)之間的模糊性),Tornado不嘗試將表單參數(shù)與其他類(lèi)型的輸入統(tǒng)一起來(lái)。特別是,我們不解析JSON請(qǐng)求體。希望使用JSON而不是表單編碼的應(yīng)用程序可能會(huì)重寫(xiě)prepare來(lái)解析其請(qǐng)求:

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

重寫(xiě)RequestHandler方法

除了?get()?/?post()?等,RequestHandler中的某些其他方法被設(shè)計(jì)為在必要時(shí)被子類(lèi)重寫(xiě)。每次請(qǐng)求時(shí),都會(huì)進(jìn)行以下順序的調(diào)用:

  1. 每個(gè)請(qǐng)求都會(huì)創(chuàng)建一個(gè)新的RequestHandler對(duì)象
  2. initialize()使用配置中的初始化參數(shù)調(diào)用Application。?initialize ?通常應(yīng)該只保存?zhèn)鬟f給成員變量的參數(shù);它可能不會(huì)產(chǎn)生任何輸出或調(diào)用方法,例如 send_error
  3. 調(diào)用prepare()。這在所有處理程序子類(lèi)共享的基類(lèi)中最有用,因?yàn)闊o(wú)論使用哪個(gè)HTTP方法,都會(huì)調(diào)用?prepare?。?prepare?可以產(chǎn)生輸出;如果它調(diào)用finish或?redirect?等等方法,處理將在此停止
  4. 其中一個(gè)HTTP方法被調(diào)用:?get()?、?post()?、?put()?等等。如果URL正則表達(dá)式包含捕獲組,則將它們作為參數(shù)傳遞給此方法
  5. 當(dāng)請(qǐng)求完成時(shí),on_finish()被調(diào)用。這通常是在?get()?或另一個(gè) HTTP 方法返回之后。

RequestHandler文件中記錄了所有設(shè)計(jì)為覆蓋的方法。一些最常用的重寫(xiě)方法包括:

  • write_error- 輸出HTML以在錯(cuò)誤頁(yè)面上使用
  • on_connection_close- 當(dāng)客戶端斷開(kāi)連接時(shí)調(diào)用;應(yīng)用程序可以選擇檢測(cè)這種情況并停止進(jìn)一步處理。請(qǐng)注意,不能保證可以立即檢測(cè)到關(guān)閉的連接
  • get_current_user- 請(qǐng)參閱用戶身份驗(yàn)證
  • get_user_locale- 返回Locale當(dāng)前用戶使用的對(duì)象
  • set_default_headers- 可用于在響應(yīng)上設(shè)置附加表頭(例如自定義?Server?表頭)

錯(cuò)誤處理

如果處理程序引發(fā)異常,Tornado將調(diào)用RequestHandler.write_error生成錯(cuò)誤頁(yè)面。tornado.web.HTTPError可用于生成指定的狀態(tài)代碼;所有其他異常都返回500狀態(tài)。

默認(rèn)錯(cuò)誤頁(yè)面包括調(diào)試模式下的堆棧跟蹤和錯(cuò)誤的單行描述(例如“500:內(nèi)部服務(wù)器錯(cuò)誤”)。要生成自定義錯(cuò)誤頁(yè),請(qǐng)重寫(xiě)RequestHandler.write_error(可能在所有處理程序共享的基類(lèi)中)。這種方法通??梢酝ㄟ^(guò)writerender等方法產(chǎn)生輸出。如果錯(cuò)誤是由異常引起的,則?exc_info ?將作為關(guān)鍵字參數(shù)傳遞(請(qǐng)注意,此異常不能保證是sys.exc_info中的當(dāng)前異常,因此?write_error?必須使用例如traceback.format_exception而不是traceback.format_exc

也可以從常規(guī)處理程序方法生成錯(cuò)誤頁(yè)面,而不是?write_error?通過(guò)調(diào)用 set_status、編寫(xiě)響應(yīng)和返回。在簡(jiǎn)單返回不方便的情況下,tornado.web.Finish可能會(huì)引發(fā)特殊異常以終止處理程序而不調(diào)用?write_error?

對(duì)于404錯(cuò)誤,使用?default_handler_class?應(yīng)用程序設(shè)置。這個(gè)處理程序應(yīng)該覆蓋prepare,而不是像???get()???這樣更具體的方法,這樣它就可以與任何HTTP方法一起工作。它應(yīng)該如上所述生成錯(cuò)誤頁(yè)面:通過(guò)引發(fā)?HTTPError(404)?并覆蓋?write_error?,或者調(diào)用?self.set_status(404)?并直接在?prepare()?中生成響應(yīng)。

重定向

在Tornado中,有兩種主要的重定向請(qǐng)求的方法:RequestHandler.redirectRedirectHandler

可以在?self.redirect()?方法中使用RequestHandler將用戶重定向到其他地方。還有一個(gè)可選參數(shù)?permanent?,可用于指示重定向被視為永久性的。?permanent?的默認(rèn)值為?False?,這將生成一個(gè)?302 Found?的HTTP響應(yīng)代碼,適用于在成功的?POST?請(qǐng)求后重定向用戶。如果?permanent?為?True?,則使用?301 Moved permanually?HTTP響應(yīng)代碼,這有助于以SEO友好的方式重定向到頁(yè)面的規(guī)范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?開(kāi)頭的請(qǐng)求重定向到前綴?/photos?:

app = tornado.web.Application([
    url(r"/photos/(.*)", MyPhotoHandler),
    url(r"/pictures/(.*)", tornado.web.RedirectHandler,
        dict(url=r"/photos/{0}")),
    ])

RequestHandler.redirect不同,RedirectHandler默認(rèn)使用永久重定向。這是因?yàn)槁酚杀碓谶\(yùn)行時(shí)不會(huì)更改,并且被認(rèn)為是永久的,而在處理程序中找到的重定向可能是其他可能更改的邏輯的結(jié)果。要使用RedirectHandler發(fā)送臨時(shí)重定向,請(qǐng)?jiān)?a rel="external nofollow" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" target="_blank" target="_blank">RedirectHandler初始化參數(shù)中添加?permanent=False?

異步處理程序

某些處理程序方法(包括?prepare()?和HTTP的?get()?/?post()?方法等)可能會(huì)被重寫(xiě)為協(xié)同路由,以使處理程序異步。

例如,下面是一個(gè)使用協(xié)同程序的簡(jiǎn)單處理程序:

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")


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)