最新Tornado5.11官方文檔翻譯(5)-用戶手冊-一個Tornado網絡應用的結構

導航

一個Tornado網絡應用的結構

Tornado Web應用程序通常由一個或多個RequestHandler子類,一個將請求路由到處理程序(handlers)的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的第三方元素被傳遞進來,它支持initialization參數,這個參數會傳遞給RequestHandler.initializa方法。最後,URLSpec可能還會有一個名字,這將使它能夠被RequestHandler.reverse_url方法使用。

例如,在此片段中,根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

基類化RequestHandler

Tornado Web應用程序的大部分工作都是在RequestHandler的子類中完成的。處理器子類的主要入口是處理對應的HTTP方法:get(),post()等等。每個處理程序可以定義一個或多個這些方法來處理不同的HTTP操作。如上所述,這些方法將會加上路徑中匹配到的參數來被調用。

在處理程序中,調用RequestHandler.renderRequestHandler.write等方法來生成響應。 render()按名稱加載模板,並使用給定的參數呈現它。 write()用於非基於模板的輸出;它接受strings,bytes和字典(dicts將被編碼爲JSON)。

RequestHandler中的許多方法都設計爲在子類中重寫,並可以在整個應用程序中使用。通常定義一個BaseHandler類來重寫諸如write_errorget_current_user之類的方法,然後把你的BaseHandler代替RequestHandler作爲所有處理器的基類。

處理請求輸入

請求處理程序可以使用self.request訪問表示當前請求的對象。有關完整的屬性列表,請參閱HTTPServerRequest的類定義。

HTML表單提交的請求數據將會爲你分析好,並且可以在一些方法像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表單編碼對於一個參數是單值還是列表是模糊不清的,RequestHandler對方法進行了區分,使得應用能夠表明是否希望獲得列表,對列表來說,使用get_query_argumentsget_body_arguments代替單值的方法。

通過表單上傳文件可以通過self.request.files來獲得,映射名字(input的name屬性)到文件列表。每個文件都是{"filename":…, "content_type":…, "body":…}格式的字典。files對象文件僅在使用表單包裝器上傳文件時才存在(例如multipart/form-data Content-Type);如果沒有使用這種格式,原生的上傳數據會存放在self.request.boby中。默認情況下上傳的文件是全部緩存在內存中的;如果你需要處理一些很大的文件,不方便放在內存中,查看stream_request_body裝飾器。

在demos文件夾中(tornado源碼中有一個demos文件夾,存放了幾個小的例子),file_reciver.py展示了這兩種方法的來接收上傳文件。

由於HTML表單編碼的問題(單值和多值的模糊性),Tornado並不打算用其他類型的輸入來統一表單參數。尤其我們不會去分析JSON請求體。希望使用JSON來代替form-encoding的應用可能會重寫prepare方法來解析它們的請求:

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

重寫RequestHandler中的方法

除了get()post()方法,在RequestHandler還有一些其他方法在必要時也可以被子類重寫。在每個請求中,都會發生以下一系列的調用:

  1. 每個請求中都會新建一個RequestHandler對象。
  2. 如果有從Application配置中獲得初始參數的話initialize()函數會被調用,initialize函數應該只保存傳遞給成員變量的參數;它可能不會產生任何輸出或調用類似send_error一樣的方法。
  3. 調用prepare方法。這在一個基類中是最有用的,基類由你的處理器子類所共享,因爲不論使用哪種HTTP方法prepare函數都會被調用。prepare可能會產生輸出;如果它調用了finish(或者redirect方法等),進程就會在此結束。
  4. 某一個HTTP方法被調用:get()post()put()等等。如果URL正則表達式包含了捕捉分組參數(capturing groups),這些參數也會傳遞到此方法中。
  5. 當請求結束時,on_finish會被調用,對於大多數處理程序,這一步在get()(或其他方法)return後就會立即執行;對於使用tornado.web.asynchronous裝飾器的處理程序,它發生在調用finish()之後。

正如在RequestHandler文檔中提到的一樣,所有方法都是可以重寫的。 一些最常被重寫的方法包括:

  • write_error- 輸出html的錯誤頁面。
  • on_connection_close當客戶端斷開連接的時候調用;應用可能會選擇檢測這種情況然後停止更深層的處理,注意,不能保證一個已關閉的連接也能被及時檢測到。 請注意,無法保證可以立即檢測到已關閉的連接。
  • get_current_user- 請參閱用戶身份驗證(User authentication)。
  • get_user_locale- 爲當前用戶返回一個locale對象。
  • set_default_headers- 可用於在response上設置其他的響應頭(例如自定義Serverheader)

錯誤處理

如果處理器拋出一個異常,tornado會調用RequestHandler.write_error來生成一個錯誤頁面。tornado.web.HTTPError可以被用來產生一個特定的狀態碼;其他所有異常都返回500狀態碼。

默認的錯誤頁面包括一個堆棧路徑(debug模式下)另外還有一個錯誤的線上描述(move brand_model.txt to project)。爲了生成一個自己的錯誤頁面,重寫RequestHandler.write_error(可能是在一個由你的所有處理器所共享的基類中)這個方法可以用過writerender等方法產生正常的輸出。如果錯誤是由一個異常導致的,一個exc_info會作爲一個關鍵字參數被傳遞進來(注意,此異常不保證是sys.exc_info中當前的異常,因此write_error必須使用traceback.format_exception來代替traceback.format_exc)。

通過調用set_status,寫一個響應或return等方法從常規的處理器方法(而不是write_error)中產生一個錯誤頁面也是可能的。tornado.web.Finish這個特殊的異常可能會被拋出以終止處理器,在簡單地返回不方便的情況下並不調用write_error方法。

對於404錯誤來說,使用default_handler_class``````Application setting。這個處理器用改重寫prepare方法,而不是更詳細的如get()方法,以便在任何HTTP方法中都能使用。它應該產生一個如上所述的錯誤頁面:或者通過拋出一個HTTPError(404)並重寫爲write_error,或者調用self.set_status(404)並在prepare()中直接產生響應。

重定向

在Tornado中有兩種方式可以重定一個請求:RequestHandler.redirectRedirectHandler

您可以在RequestHandler方法中使用self.redirect()將用戶重定向到其他位置。還有一個可選參數permanent,可用於表明該重定向爲永久重定向。 permanent的默認值爲False,它生成302 FoundHTTP響應代碼,適用於成功POST請求後重定向用戶等事項。 如果permanenttrue301 Moved PermanentlyHTTP響應碼會被使用,在下面這中情況下是有用的:重定向到一個權威的URL來採用一種SEO友好的方式獲取頁面。

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/{0}")),
    ])

不像RequestHandler.redirectRedirectHandler默認使用永久重定向。這是因爲路由表在運行過程中不會改變並且是永久的,儘管在處理器中的重定向很可能是其他可能回改變的邏輯所導致的。如果想使用RedirectHandler發起一個臨時重定向,只需要把permanent=False參數加到RedirectHandler的初始化參數中。

異步處理器

某些處理程序方法(包括prepare()和HTTP請求方法get()post()等)可以作爲協程重寫,以實現異步化。

Tornado還支持使用tornado.web.asynchronous裝飾器那樣基於回調的異步處理程序樣式,但這種樣式已棄用,將在Tornado 6.0中刪除。新的應用程序應該使用協程代替舊的寫法。

下面是一個簡單的使用協程的handler示例:

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

有關更高級的異步示例,請查看聊天示例應用程序,該應用程序使用長輪詢實現AJAX聊天室。 長輪詢的用戶可能希望在客戶端關閉連接後重寫on_connection_close()以進行清理操作(但要注意的是,請參見該方法的docstring)。
上一篇:Queue示例 - 一個併發的網絡爬蟲

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章