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. 對外部屬性跟內部屬性(帶下划線)的賦值。注意:從源碼可以看出application為RequestHandler類實例對象的屬性
  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

</span><span style="color: #0000ff">def</span> get(self, *args, **<span style="color: #000000">kwargs):
    </span><span style="color: #0000ff">print</span><span style="color: #000000">(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
</span><span style="color: #0000ff">def</span> get(self, *args, **<span style="color: #000000">kwargs):
    </span><span style="color: #008000">#</span><span style="color: #008000">print(self.welcome + "\r\n" + "and value2 =" + self.value2)</span>
    <span style="color: #0000ff">print</span>(<span style="color: #800000">"</span><span style="color: #800000">get method: Processing get Method...........</span><span style="color: #800000">"</span><span style="color: #000000">)
    self.write(self.welcome </span>+ <span style="color: #800000">"</span><span style="color: #800000">\r\n</span><span style="color: #800000">"</span> + <span style="color: #800000">"</span><span style="color: #800000">and value2 =</span><span style="color: #800000">"</span> +<span style="color: #000000"> self.value2)

</span><span style="color: #0000ff">def</span><span style="color: #000000"> set_default_headers(self):
    self._headers.add(</span><span style="color: #800000">"</span><span style="color: #800000">custom_header1</span><span style="color: #800000">"</span>, <span style="color: #800000">"</span><span style="color: #800000">custom_header1</span><span style="color: #800000">"</span><span style="color: #000000">)
    self._headers.add(</span><span style="color: #800000">"</span><span style="color: #800000">custom_header2</span><span style="color: #800000">"</span>, <span style="color: #800000">"</span><span style="color: #800000">custom_header2</span><span style="color: #800000">"</span><span style="color: #000000">)

</span><span style="color: #0000ff">def</span><span style="color: #000000"> prepare(self):
    </span><span style="color: #0000ff">print</span>(<span style="color: #800000">"</span><span style="color: #800000">prepare method:initilize some variables....</span><span style="color: #800000">"</span><span style="color: #000000">)

</span><span style="color: #0000ff">def</span><span style="color: #000000"> on_finish(self):
    </span><span style="color: #0000ff">print</span>(<span style="color: #800000">"</span><span style="color: #800000">on_finish method:clear some memory or close database connection</span><span style="color: #800000">"</span>)</pre>
復制代碼

      執行的結果如下:

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)
<span style="color: #008000">#</span><span style="color: #008000"> print(self.request.query_arguments)</span>
<span style="color: #008000">#</span><span style="color: #008000"> print(self.request.arguments)</span>
<span style="color: #008000">#</span><span style="color: #008000"> print(self.request.body_arguments)</span>
<span style="color: #008000">#</span><span style="color: #008000"> self.write("參數name的值為:" + self.get_argument("name", "liaofei"))</span>

<span style="color: #0000ff">print</span>(1/0)</pre>
復制代碼
   

             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

分類: Python Tornado
0
0
« 上一篇: tornado 學習筆記6 Application 源碼分析
» 下一篇: tornado 學習筆記8 模板以及UI
	</div>
	<div class="postDesc">posted @ <span id="post-date">2015-11-05 11:48</span> <a href="https://www.cnblogs.com/liaofeifight/">廖飛</a> 閱讀(<span id="post_view_count">2934</span>) 評論(<span id="post_comment_count">0</span>)  <a href="https://i.cnblogs.com/EditPosts.aspx?postid=4938987" rel="nofollow">編輯</a> <a href="#" onclick="AddToWz(4938987);return false;">收藏</a></div>
</div>
posted @ 2018-11-23 22:20  silence-cc  閱讀( 658)  評論( 1編輯  收藏


免責聲明!

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



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