tornado五:tornado.web.RequestHandler請求與響應


一、請求

http://www.baidu.com:8080/path1/path2/file.html?a=1&b=2#abc

客戶端請求流程:查詢本地hosts文件,如果沒有主機名www.baidu.com對應的ip,從cdn服務器查義主機名對應的ip,找到,則訪問到服務器。再根據路徑和文件名,訪問到/path1/path2/file.html文件;查詢參數為:a=1&b=2,錨為abc。瀏覽器接收到此文件以后,顯示在瀏覽上。

 

tornado.web.RequestHandler的作用:

1.利用HTTP協議向服務器傳遞參數。

  其傳遞參數的方式有:

  • 提取uri的特定部分
      • 示例:網址如http://127.0.0.1/liuyf/aaa/bb/c,http://127.0.0.1/liuyf為固定部分,路徑/aaa/bb/c為可變部分。要實現此功能,需要提取uri部分作為參數。
        # 路由1:
         (r'/liuyf/(\w+)/(\w+)/(\w+)', index.LiuyfHandler)
        # 路由2:
        (r'/liuyf/(?P<p1>\w+)/(?P<p3>\w+)/(?P<p2>\w+)', index.LiuyfHandler),
        # 兩種路由方式都可匹配下面的視圖函數
        # handler:
        class LiuyfHandler(RequestHandler):
          # 接收uri參數, 在get、post等請求函數的參數中接收。
        def get(self, h1, h2, h3, *args, **kwargs): print h1,h2,h3 self.write('這是liuyf')
  • 查詢字符串,get方式傳遞參數
    • 網址:http://127.0.0.1/zhangmy?a=1&b=2&c=3
      # 路由一樣:
                  (r'/zhangmy', index.ZhangmyHandler)
      #Handler:
      class ZhangmyHandler(RequestHandler):
          def get(self,*args, **kwargs):
              # get_argument方法的原型:self.get_argument(name, default=ARG_DEFAULT, strip=True)
              # name:從get請求參數字符串中返回指定參數的值。如果出現多個同名參數,這個方法會返回最后一個值。
              # default:設置未傳的name參數的默認值。如果name未傳,default也未設置,會拋出tornado.web.MissingArgument異常
              # strip:表示是否過濾掉參數值的左右空白字符,默認為True。通常情況下不需要空格字符,但是在搜索等情況下需要空格。
              a = self.get_query_argument("a")
              b = self.get_query_argument("b")
              c = self.get_query_argument("c")
              print "*"+a+"*","*"+b+"*","*"+c+"*"
              self.write('這是zhangmy')

       

    • 網址:http://127.0.0.1/zhangmy?a=1&a=2&a=3 
      class ZhangmyHandler(RequestHandler):
          def get(self,*args, **kwargs):
              # 如果有多個同名參數,使用get_argument會獲取到最后一個值。
              # 如果想要多個值都獲取,要使用get_arguments,返回列表。
              # get_arguments方法的原型:self.get_argument(name, strip=True)
              a = self.get_query_arguments("a")
              print "*"+a+"*"
              self.write('這是zhangmy')

       

       
  • 請求體攜帶數據,post方式傳遞參數
    • 示例:
    • 1.創建路由
                  (r'/mylike', index.MyLikeHandler)
    • 2.創建一個提交數據的頁面
    • <!DOCTYPE html>
      <html lang="en">
      <head>
          <meta charset="UTF-8">
          <title>select my like</title>
      </head>
      <body>
      <form action="/mylike" method="post">
          姓名:<input type="text" name="name"/>
          <hr/>
          密碼:<input type="password" name="passwd"/>
          <hr/>
          愛好:
          <input type="checkbox" value="power" name="mylike">權利
              <input type="checkbox" value="money" name="mylike">金錢
              <input type="checkbox" value="book" name="mylike"><input type="submit" value="登陸">
      </form>
      
      </body>
      </html>

       在post請求,提交數據之前:自動使用get方法返回上面的頁面。頁面渲染使用self.render(模板)。

      #Handler:
      class MyLikeHandler(RequestHandler):
          def get(self, *args, **kwargs):
              # 返回模板
              self.render("postfile.html")

       

       
    • 處理post請求參數:
      class MyLikeHandler(RequestHandler):
          def get(self, *args, **kwargs):
              # 返回模板
              self.render("postfile.html")
          def post(self, *args, **kwargs):
              # 接收post參數
              # 1.使用get_body_argument,原型為:
              # self.get_body_argument(name, default=ARG_ARGUMENT, strip=True)
              # 2.如果存在多個同名參數,想取所有值,使用get_body_arguments,返回列表,原型為:
              # self.get_body_arguments(name, strip=True)
              name = self.get_body_argument("name")
              passwd = self.get_body_argument("passwd")
              likeList = self.get_body_arguments("like")
              print name, passwd, likeList
              self.write("提交成功!")

       

  • 既可以獲取get請求,也可以獲取post請求的參數
    • get_argument(name, default=ARG_DEFAULT, strip=True)和get_arguments(name, default=ARG_DEFAULT, strip=True)既可以獲取get請求參數,也可以獲取post請求參數。以上面的示例為例:
      class MyLikeHandler(RequestHandler):
          def get(self, *args, **kwargs):
              # 返回模板
              self.render("postfile.html")
          def post(self, *args, **kwargs):
              # 同時接收get和post參數
              # 1.使用get_argument,原型為:
              # self.getargument(name, default=ARG_ARGUMENT, strip=True)
              # 2.如果存在多個同名參數,想取所有值,使用get_arguments,返回列表,原型為:
              # self.get_arguments(name, strip=True)
              a_list = self.get_arguments("a")
              b = self.get_argument("b")
              c = self.get_argument("c")
              name = self.get_argument("name")
              passwd = self.get_argument("passwd")
              likeList = self.get_arguments("like")
              print name, passwd, likeList
              self.write("提交成功!")

      以上的缺點,不能區分是get還是post的參數;優點,可以同時獲取get和post的參數。

  • 在http報文的頭中增加自定義的字段:略

2.request對象--請求對象

  作用:存儲了關於請求的相關信息。

  它擁有的屬性:

  method:是HTTP請求的方式

  host:被請求的服務器名稱

  uri:請求的完整資源地址,包括路徑和get查詢參數部分:/path1/path2/file.html?a=1&b=2

  path:請求的路徑部分:/path1/path2

  query:請求參數部分:a=1&b=2

  version:使用的HTTP版本

  headers:請求的協議頭,是一個字典類型

  body:請求體的數據,post請求才有請求體。

  remote_ip:客戶端的ip

  files:用戶上傳的文件,是一字典類型

#路由
            ('r/haha', index.HahaHandler),
# 視圖處理Handler
class HahaHandler(RequestHandler):
    def get(self, *args, **kwargs):
        print self.request.method
        print self.request.uri
        print self.request.path
        print self.request.query
        print self.request.version
        print self.request.headers
        print self.request.body
        print self.request.remote_ip
        print self.request.files

 

3.tornador.httputil.HTTPFile對象:是接收到的文件對象,每上傳一個文件,生成一個文件對象。

當上傳文件的時侯,才有此文件對象。

此文件對象的屬性:

  •   filename:文件的實際名字
  •   body屬性:文件的數據實體,即數據內容
  •   content_type:文件的類型

上傳文件的過程:

1).get展示頁面;

2).post請求上傳文件;

3).在服務器上創建一個同名同類型文件;

4).將tornador.httputil.HTTPFile對象的body內容寫入服務器上的同名文件。

  創建一個上傳文件的頁面:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>上傳文件</title>
</head>
<body>
<form method="post" action="/linqx" enctype="multipart/form-data">
    <input type="file" name="file">
    <input type="file" name="file">
    <input type="file" name="img">
    <input type="submit" value="上傳">
</form>

</body>
</html>

 

  創建上傳文件的路由:

            (r'/linqx', index.UpfileHandler)

 

  視圖函數Handler:

class UpFileHandler(RequestHandler):
    def get(self, *args, **kwargs):
        # 展示上傳文件的頁面
        self.render("upfile.html")
    def post(self, *args, **kwargs):
        filesDict = self.request.files
        # files 就是tornador.httputil.HTTPFile對象
        print filesDict
     # 最外層字典的key是html文件中的input標簽的name的值,可以為任意值。
"""{"file": [{ "filename": "a.txt", "body": b"this is content.........", "content_type": "text/plain" }, { "filename": "b.txt", "body": b"this is b's content.........", "content_type": "text/plain" },], "img":[{ "filename": "圖片.png", "body": b"weradfs342r.........", "content_type":"image/png" },], }""" # 將文件寫入到服務器上 import os import config for inputname in filesDict: fileArr = filesDict[inputname] for fileObj in fileArr: filePath = os.path.join(config.BASE_DIRS, 'upfile/' + inputname) with open(filePath, 'wb') as f: f.write(fileObj.body) self.write("上傳文件成功!")

 二、響應

響應中的方法,也在RequestHandler對象中。

帶有self的方法,是RequestHandler已實現的方法,是實例方法,直接使用。

未帶self的方法,是RequestHandler未實現的方法,需要我們重寫的方法,如果有需要。

1.self.wirte:

原型self.wirte(chunk)。

作用:將chunk數據寫到輸出緩沖區,對應的響應socket將緩沖區的數據返回給瀏覽器。

  緩沖區刷新條件:1.程序結束;2.手動刷新;3.緩沖區滿了;遇到/n

 

 2.self.finish(chunck=None),默認chunk為None

作用:刷新緩沖區,並關閉當次請求通道。

因此,self.finish()后面的self.write('asdf')內容雖然會寫入到緩沖區,但是請求通道已關閉,不會返回給用戶,沒有意義了。

 

3.利用self.wirte('.....')寫json,返回給ajax,是動態的刷新給的。如,瀏覽器向下拖動的數據,是后來加載的。

返回json數據給瀏覽器方式一,手動轉換數據為json字符串:

# json
('r/json1', index.Json1Handler)

 

class Json1Handler(RequestHandler):
    def get(self, *args, **kwargs):
        per = {
            "name": "haha",
            "age": 23,
            "heigh": 175,
            "weight":70
        }
        import json
        jsonStr = json.dumps(per)
        self.write(jsonStr)

 

返回json數據給瀏覽器方式二:

self.writer(),默認會轉換為json字符串返回。

class Json1Handler(RequestHandler):
    def get(self, *args, **kwargs):
        per = {
            "name": "haha",
            "age": 23,
            "heigh": 175,
            "weight":70
        }
        self.write(per)

 

區別:方式一手動序列化時,返回的headers中的content_type為application/html;

方式二使用write自動序列化時,返回的headers中的content_type為application/json;

因此,希望返回json數據時,使用第二種方式self.write自動序列化。

 

4.self.set_header設置響應頭(Response Headers)

作用:如,手動設置一個名為username,值為xxxx的響應頭字段

參數:name,字段名稱;value字段值

示例:將3中的第一種方式的content_type改為application/json;charset=UTF-8

class Json1Handler(RequestHandler):
    def get(self, *args, **kwargs):
        per = {
            "name": "haha",
            "age": 23,
            "heigh": 175,
            "weight":70
        }
        import json
        jsonStr = json.dumps(per)
        self.set_header("Content-Type", "application/json;charset=UTF-8")
        self.set_header("heihei", "heihei's value")
        self.write(jsonStr)

 

5.set_default_headers()設置響應頭

通常,要修改響應頭,重寫此方法,而不是在隨處任寫;在此方法中,調用self.set_header()去設置響應頭。

作用:此方法,在進入HTTP響應處理之前調用,可以重寫該 方法來預先設置響應頭。

注意:如果在多處,寫了self.set_header()方法;后調用的方法會覆蓋先調用的方法。

class HeaderHandler(RequestHandler):
    def set_default_headers(self):
        self.set_header("Content-Type", "application/json;charset=UTF-8")
        self.set_header("heihei", "1")
    def get(self, *args, **kwargs):
        self.set_header("heihei", "2")
        self.write("test set_header")

 

6.self.set_status(status_code, reason=None)設置響應狀態碼

作用:為響應設置狀態碼

參數:status_code,狀態碼值,為int整型;reason,描述狀態碼的詞組,string類型。

  如果reason的值為None,則狀態碼必須為系統定義的值。

1).使用已有的(系統定義的)狀態碼:

class StatusHandler(RequestHandler):
    def get(self, *args, **kwargs):
        self.set_status(404)
        self.write('sdaf.........')

 

2).使用自定義的狀態碼:

此時,reason不能為None,否則將拋出異常。

class StatusHandler(RequestHandler):
    def get(self, *args, **kwargs):
        self.set_status(999, "who?")
        self.write('sdaf.........')

 

7.self.redirect(url)重定向

作用:重定向到url網址

            # 重定向
            (r'/redirect', index.RedirectHandler),

class RedirectHandler(RequestHandler):
    def get(self, *args, **kwargs):
        self.redirect("/")

 

8.self.send_error(status_code=500, **kwargs): 用以拋出錯誤狀態碼

作用:拋出HTTP錯誤狀態碼,默認為500。拋出錯誤后tornado會調用writer_error()方法進行處理,並返回給瀏覽器錯誤界面。

9.write_error(status_code, **kwargs):用來處理拋出的錯誤

作用:用來處理send_error拋出的錯誤信息,並並返回給瀏覽器錯誤界面。同上面的方法一起使用。

# 路由
            # 錯誤處理
            #iserror?flag=2,假設flag為0為錯誤
            (r'/iserror', index.ErrorHandler)
# Handler
class ErrorHandler(RequestHandler):
    def write_error(self, status_code, **kwargs):
        if status_code == 500:
            self.set_status(500)
            # 或者返回自定義的頁面
            self.write("服務器內部錯誤")
        elif status_code == 404:
            self.set_status(404)
            self.write("資源不存在")
        else:
            self.set_status(999, "what")
            self.write("未知錯誤")
    def get(self, *args, **kwargs):
        flag = self.get_query_argument("flag")
        if flag == '0':
            self.send_error(500)
        self.write("your are right!")

 

三、請求與響應流程

紅色邊框:tornado-IOLoop

黑色邊框:linux服務器范圍:

  • 當tornado httpserver服務器創建成功以后,linux服務器為http服務器創建socket,創建后將socket交給linux服務器管理,負責監聽客戶端的請求。
  • tornado-IOLoop死循環,與linux-epoll交互,詢問,是否有新的請求到來。
  • 當有客戶端請求到來時,linux服務器為每個客戶端創建一個獨立的socket,如socket1,socket2。
  • 並交由tornado處理

藍色箭頭:請求流程

每個客戶端請求,使用獨立的套接字,如socket1。沿着藍色箭頭,經過socket1,經路由Application處理,交給視圖處理函數Handler處理。同步。

黃色箭頭:響應流程

Handler收到請求並處理后,沿着黃色箭頭,異步返回給客戶端響應套接字socket1緩沖區,socket1刷新緩沖區數據返回給瀏覽器。

 


免責聲明!

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



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