tornado 學習筆記7 RequestHandler功能分析


       在第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)
  1. 對外部屬性跟內部屬性(帶下划線)的賦值。
  2. 然后對ui的處理,這個暫時不做深入細讀。
  3. self.clear() 調用clear方法,初始化一些屬性。
  4. 設置請求連接關閉時的回調函數。
          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的值。

          結果如下圖:

clip_image002

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

      執行的結果如下:

clip_image002[5]

       所以得出執行的順序為:

       initialize > prepare > get > on_finish

       如果有熟悉Java 的JUnit的話呢,prepare跟on_finish方法相當於before跟behind兩個注解的功能。

獲得輸入的函數:

7.4  get_argument、get_arguments方法

        返回給定參數的值,get_argument獲得單個值,而get_arguments是針對參數存在多個值得情況下使用,返回多個值的列表。看一get_arguments方法的源代碼,如下:

clip_image004

          它的實現是調用了內部方法_get_arguments,注意傳遞的參數self.request.arguments,從request(HTTPServerRequest對象)的arguments屬性中去查詢給定名稱的值。看看HTTPServerRequest源代碼(位於tornado>httputil.py)對arguments的解釋,如下截圖:

clip_image006

          大體意思就是說,這里存儲的是客戶端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,結果如下圖所示:

clip_image008

          在舉個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數據提交,我找了很久用了,找到如下工具(很多工具不能用,這個稍微能用一下),

clip_image010

          后台打印的結果為:clip_image012,HTTPRequest 的arguments屬性是一個字典。

         提一個問題?如果URL中帶有age查詢參數,而post過去也有age參數,這時HTTPRequest 的arguments中age的值會是什么???測試一下便知。按照如下訪問,

clip_image014

        后台打印的結果為:clip_image016,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"))

        按如下方式模擬訪問:

clip_image018

           后台打印的結果如下圖:

clip_image020

           所以得出結論就是,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]

      處理過程

  1. 首先默認設置3個響應頭部,分別是Server,Content_Type、Date.
  2. 調用set_default_headers 方法,實現自定義header的設置。
  3. 其他參數的默認設置。

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查詢響應頭。結果如下:

clip_image022

7.10 write方法

        將給定的塊輸出到輸出緩存中。如果給定的塊是一個字典,就會將這個快當成是JSON並且將Content_Type設置成application/json返回給客戶端。但是,如果你想用不同的Content_Type發送JSON,可以在調用write方法后再調用set_header方法進行設置。

       注意,list 列表集合是不會轉化成JSON的,原因是考慮到跨域的安全。所有的JSON輸出都必須用字典包裝。具體源碼說明如下:

clip_image024

7.11 rend 方法

        用給定的參數渲染模板。這個涉及到模板的概念,后續再學。

7.12  write_error方法

        重寫自定義錯誤頁面的實現。

        如果error是由沒有捕獲的異常(包括HTTPError)引起的,通過kwargs[|”exc_info”]能獲取exc_info元組。實現代碼如下:

clip_image026

            舉個例子實現一個自定義的錯誤頁面,在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,得到如下結果,

clip_image028

            就是使用了重寫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),發現在游覽器客戶端的值是不一樣的,一個加密過,一個未加密。具體結果如下圖:

clip_image030


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM