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

2022-03-08 10:40 更新

Tornado Web 應(yīng)用程序通常由一個或多個 RequestHandler子類,一個Application將傳入請求路由到處理程序的對象和一個?main()?啟動服務(wù)器的函數(shù)組成。

一個最小的“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對象負責(zé)全局配置,包括將請求映射到處理程序的路由表。

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

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

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

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

處理請求輸入

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

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

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

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

由于HTML表單編碼的特殊性(例如,單數(shù)和復(fù)數(shù)參數(shù)之間的模糊性),Tornado不嘗試將表單參數(shù)與其他類型的輸入統(tǒng)一起來。特別是,我們不解析JSON請求體。希望使用JSON而不是表單編碼的應(yīng)用程序可能會重寫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中的某些其他方法被設(shè)計為在必要時被子類重寫。每次請求時,都會進行以下順序的調(diào)用:

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

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

錯誤處理

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

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

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

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

重定向

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

可以在?self.redirect()?方法中使用RequestHandler將用戶重定向到其他地方。還有一個可選參數(shù)?permanent?,可用于指示重定向被視為永久性的。?permanent?的默認值為?False?,這將生成一個?302 Found?的HTTP響應(yīng)代碼,適用于在成功的?POST?請求后重定向用戶。如果?permanent?為?True?,則使用?301 Moved permanually?HTTP響應(yīng)代碼,這有助于以SEO友好的方式重定向到頁面的規(guī)范URL

RedirectHandler允許您直接在Application路由表中配置重定向。例如,要配置單個靜態(tài)重定向:

app = tornado.web.Application([
    url(r"/app", tornado.web.RedirectHandler,
        dict(url="http://itunes.apple.com/my-app-id")),
    ])

RedirectHandler還支持正則表達式替換。以下規(guī)則將所有以?/pictures?開頭的請求重定向到前綴?/photos?:

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

RequestHandler.redirect不同,RedirectHandler默認使用永久重定向。這是因為路由表在運行時不會更改,并且被認為是永久的,而在處理程序中找到的重定向可能是其他可能更改的邏輯的結(jié)果。要使用RedirectHandler發(fā)送臨時重定向,請在RedirectHandler初始化參數(shù)中添加?permanent=False?

異步處理程序

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

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

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)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號