在第5部分講到,構建一個tornado網站,必須包含一個或者多個handler,這些handler是RequestHandler的子類。每個請求都會被映射到handler中進行處理,處理后再將結果返回給客戶端。所以,可以看到hanlder作為客戶端請求跟業務服務邏輯間的橋梁,如果拿MVC的模式來類比的話,每個handler就相當於MVC中的Controller。
RequestHanlder作為所有hanlder的父類,我們看看他有哪些方法與接口,子類需要怎樣繼承?
7.1構造函數
定義:
def __init__(self, application, request, **kwargs):
參數:
application: Application對象
request: request請求對象
kwargs:其他參數,在hanlder映射配置時,可以設置。
處理過程:
super(RequestHandler, self).__init__() self.application = application self.request = request self._headers_written = False self._finished = False self._auto_finish = True self._transforms = None # will be set in _execute self._prepared_future = None self.path_args = None self.path_kwargs = None self.ui = ObjectDict((n, self._ui_method(m)) for n, m in application.ui_methods.items()) # UIModules are available as both `modules` and `_tt_modules` in the # template namespace. Historically only `modules` was available # but could be clobbered by user additions to the namespace. # The template {% module %} directive looks in `_tt_modules` to avoid # possible conflicts. self.ui["_tt_modules"] = _UIModuleNamespace(self, application.ui_modules) self.ui["modules"] = self.ui["_tt_modules"] self.clear() self.request.connection.set_close_callback(self.on_connection_close) self.initialize(**kwargs)
- 對外部屬性跟內部屬性(帶下划線)的賦值。
- 然后對ui的處理,這個暫時不做深入細讀。
- self.clear() 調用clear方法,初始化一些屬性。
- 設置請求連接關閉時的回調函數。
self.request.connection.set_close_callback(self.on_connection_close)
5. 調用初始化函數。
self.initialize(**kwargs)
這個被子類繼承,針對每個hanlder實現自己的初始化過程。
入點函數
7.2 initialize方法
該方法被子類重寫,實現初始化過程。參數的來源於配置Application時,傳遞的參數。舉例如下:
# -*- coding: utf-8 -*- from tornado.ioloop import IOLoop from tornado.web import Application, RequestHandler __author__ = 'Administrator' class BookRequestHandler(RequestHandler): def initialize(self, welcome): self.welcome = welcome def get(self, *args, **kwargs): print(self.welcome) self.write(self.welcome) welcome = "**大學圖書館" app = Application(handlers=[ (r"/book", BookRequestHandler, dict(welcome=welcome)), ]) def main(): app.listen(8888) IOLoop.current().start() if __name__ == "__main__": main()
將welcome初始化的值傳遞到BookRequestHandler的self.welcome屬性中。當訪問http://localhost:8888/book時,打印出welcome的值。
結果如下圖:
7.3 prepare 、 on_finish方法
prepare方法用於當真正調用請求處理方法之前的初始化處理,比如get、post方法。而on_finish用於請求處理結束后的一些清理工作。這兩個方法一個在處理前,一個在處理后,可以根據實際需要進行重寫。比如用prepare方法做些初始化操作(比如賦值設置全局變量,比如打開I/O),而on_finish方法可以做一些清理對象占用的內存或者關閉數據庫連接等等。舉個例子,來證明他們的執行順序。
class BookRequestHandler(RequestHandler): def initialize(self, welcome,value2): print("initialize method:initilize some variables....") self.welcome = welcome self.value2=value2 def get(self, *args, **kwargs): #print(self.welcome + "\r\n" + "and value2 =" + self.value2) print("get method: Processing get Method...........") self.write(self.welcome + "\r\n" + "and value2 =" + self.value2) def set_default_headers(self): self._headers.add("custom_header1", "custom_header1") self._headers.add("custom_header2", "custom_header2") def prepare(self): print("prepare method:initilize some variables....") def on_finish(self): print("on_finish method:clear some memory or close database connection")
執行的結果如下:
所以得出執行的順序為:
initialize > prepare > get > on_finish
如果有熟悉Java 的JUnit的話呢,prepare跟on_finish方法相當於before跟behind兩個注解的功能。
獲得輸入的函數:
7.4 get_argument、get_arguments方法
返回給定參數的值,get_argument獲得單個值,而get_arguments是針對參數存在多個值得情況下使用,返回多個值的列表。看一get_arguments方法的源代碼,如下:
它的實現是調用了內部方法_get_arguments,注意傳遞的參數self.request.arguments,從request(HTTPServerRequest對象)的arguments屬性中去查詢給定名稱的值。看看HTTPServerRequest源代碼(位於tornado>httputil.py)對arguments的解釋,如下截圖:
大體意思就是說,這里存儲的是客戶端GET/POST方法提交上來的合並后的參數列表集合。也就是說RequestHanlder的get_arguments方法是能獲得不管是Get還是POST得參數的值。舉個GET提交參數的例子,
修改BookRequestHanlder的get方法。如下:
def get(self, *args, **kwargs): #print(self.welcome + "\r\n" + "and value2 =" + self.value2) print("get method: Processing get Method...........") #self.write(self.welcome + "\r\n" + "and value2 =" + self.value2) self.write("參數name的值為:" + self.get_argument("name", "liaofei"))
向游覽器中打印出參數name的值,游覽器中訪問:http://localhost:8888/book?name=brain,結果如下圖所示:
在舉個POST方式提交參數的例子,在BookRequestHanlder 中新增POST方法,如下:
def post(self, *args, **kwargs): print(self.request.arguments) print("POS age:" + self.get_argument("age")) self.write("POS age:" + self.get_argument("age"))
網上下載一個模擬工具進行POST數據提交,我找了很久用了,找到如下工具(很多工具不能用,這個稍微能用一下),
后台打印的結果為:,HTTPRequest 的arguments屬性是一個字典。
提一個問題?如果URL中帶有age查詢參數,而post過去也有age參數,這時HTTPRequest 的arguments中age的值會是什么???測試一下便知。按照如下訪問,
后台打印的結果為:,age的值是一個列表集合,將POST提交方式age參數值跟GET提交方式age參數值合並啦,而且是GET在前,POST再后。而get_arguments獲得最后一個。
7.5 get_query_argument
、get_query_arguments方法
與get_argument、get_arguments功能類似,只是他僅僅是從URL查詢參數中獲取值。
7.6 get_body_argument
、get_body_arguments方法
與get_argument、get_arguments功能類似,只是他僅僅是從body中獲取值。
再提一個問題?????get_query_argument、get _argument、get_body_argument的區別????測試一下便知。
按如下修改post方法,
def post(self, *args, **kwargs): print(self.request.query_arguments) print(self.request.arguments) print(self.request.body_arguments) print("POS age:" + self.get_argument("age")) self.write("POS age:" + self.get_argument("age"))
按如下方式模擬訪問:
后台打印的結果如下圖:
所以得出結論就是,get _argument獲取的值范圍是get_query_argument與get_body_argument兩者范圍的合並。
輸出響應函數:
7.7 clear方法
重置響應的所有的頭部以及內容。
"""Resets all headers and content for this response.""" self._headers = httputil.HTTPHeaders({ "Server": "TornadoServer/%s" % tornado.version, "Content-Type": "text/html; charset=UTF-8", "Date": httputil.format_timestamp(time.time()), }) self.set_default_headers() self._write_buffer = [] self._status_code = 200 self._reason = httputil.responses[200]
處理過程
- 首先默認設置3個響應頭部,分別是Server,Content_Type、Date.
- 調用set_default_headers 方法,實現自定義header的設置。
- 其他參數的默認設置。
7.9 set_default_headers方法
上面說了set_default_headers 方法是實現請求響應頭部的自定義實現,被子類重寫。舉個例子。
在上面的BookRequestHandler中加入以下代碼
def set_default_headers(self): self._headers.add("custom_header1", "custom_header1") self._headers.add("custom_header2", "custom_header2")
通過游覽器訪問http://localhost:8888/book,用firebug查詢響應頭。結果如下:
7.10 write方法
將給定的塊輸出到輸出緩存中。如果給定的塊是一個字典,就會將這個快當成是JSON並且將Content_Type設置成application/json返回給客戶端。但是,如果你想用不同的Content_Type發送JSON,可以在調用write方法后再調用set_header方法進行設置。
注意,list 列表集合是不會轉化成JSON的,原因是考慮到跨域的安全。所有的JSON輸出都必須用字典包裝。具體源碼說明如下:
7.11 rend 方法
用給定的參數渲染模板。這個涉及到模板的概念,后續再學。
7.12 write_error方法
重寫自定義錯誤頁面的實現。
如果error是由沒有捕獲的異常(包括HTTPError)引起的,通過kwargs[|”exc_info”]能獲取exc_info元組。實現代碼如下:
舉個例子實現一個自定義的錯誤頁面,在BookRequestHandler中添加如下代碼:
def write_error(self, status_code, **kwargs): self.write("oh,my god!出錯啦!!!!請聯系系統管理員。\n") self.write("呵呵,也沒關系,我已經講錯誤信息記錄在日志中去了,系統管理員會看到的。\r\n") if "exc_info" in kwargs: self.write("錯誤信息為:\r\n") for line in traceback.format_exception(*kwargs["exc_info"]): self.write(line) self.finish()
並將get方法修改成:
def get(self, *args, **kwargs): # print(self.welcome + "\r\n" + "and value2 =" + self.value2) print("get method: Processing get Method...........") # self.write(self.welcome + "\r\n" + "and value2 =" + self.value2) # print(self.request.query_arguments) # print(self.request.arguments) # print(self.request.body_arguments) # self.write("參數name的值為:" + self.get_argument("name", "liaofei")) print(1/0)
print(1/0)語句,人為地制造一個錯誤。在游覽器中訪問http://localhost:8888/book,得到如下結果,
就是使用了重寫write_error中的方法實現。
Cookie相關函數
7.13
get_cookie
、set_cookie
獲取與設置cookie的值。這個對熟悉web開發的人,一看就明白。就不多解釋。
7.14
get_secure_cookie
、set_secure_cookie
獲取與設置安全cookie的值。與7.13 相比加了一個單詞secure(安全),就說明是為cookie安全加密解密的作用。這個方法涉及到
cookie_secret 的Application 設置選項。來舉個列子說明他們之間的區別:
加入以下代碼:
class CookieRequestHandler(RequestHandler): def get(self, flag): if flag == '0': self.set_cookie("user", "liaofei") elif flag == '1': userCookie = self.get_cookie("user") self.write(userCookie)
修改application的配置
settings = { "debug":False, "cookie_secret":"gagagaarwrslijwlrjoiajfoajgojowurwojrowjojgfoaguuuu9", } app = Application(handlers=[ (r"/book", BookRequestHandler, dict(welcome=welcome, value2=value2)), (r"/cookie/([0-1]+)", CookieRequestHandler), ], **settings) (r"/cookie/([0-1]+)", CookieRequestHandler), ], **settings)
注意settings中設置了cookie_secret的值。
訪問http://localhost:8888/cookie/0時,調用set_cookie
設置cookie中user的值為liaofei,並調用set_secure_cookie
設置cookie 中usersecure的值同樣為liaofei。用firebug查看這個相同值得cookie(都是liaofei),發現在游覽器客戶端的值是不一樣的,一個加密過,一個未加密。具體結果如下圖: