Python探析get和post方法

hiekay發表於2019-01-10

在開發網站的過程中,post和get是常見常用的兩個方法,關於這兩個方法的詳細解釋,請列為閱讀這篇文章:《HTTP POST GET 本質區別詳解》,這篇文章前面已經推薦閱讀了,可以這麼說,如果沒有搞明白get和post,也可以寫出web程式,但是如果要對這方面有深入理解,並且將來能上一個檔次,是必須瞭解的。

看下面的程式碼先:

#!/usr/bin/env python
#coding:utf-8

import textwrap

import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado.web

from tornado.options import define, options
define("port", default=8000, help="Please send email to me", type=int)

class ReverseHandler(tornado.web.RequestHandler):
    def get(self, input_word):
        self.write(input_word[::-1])

class WrapHandler(tornado.web.RequestHandler):
    def post(self):
        text = self.get_argument("text")
        width = self.get_argument("width", 40)
        self.write(textwrap.fill(text, width))

if __name__ == "__main__":
    tornado.options.parse_command_line()
    app = tornado.web.Application(
        handlers = [
            (r"/reverse/(w+)", ReverseHandler),
            (r"/wrap", WrapHandler)
        ]
    )
    http_server = tornado.httpserver.HTTPServer(app)
    http_server.listen(options.port)
    tornado.ioloop.IOLoop.instance().start()

這段程式碼跟上一講的程式碼相比,基本結構是一樣的,但是在程式主體中,這次寫了兩個類ReverseHandlerWrapHandler,這兩個類中分別有兩個方法get()和post()。在tornado.web.Application()例項化中,handlers的引數值分別設定了不同路徑對應這兩個類。

其它方面跟上一講的程式碼一樣。

把上述程式碼的檔案,存到某個目錄下,我給他取名為:request_url.py,名字也可以自己定。然後進入該目錄,執行:python request_url.py,就將這個tornado框架的網站以埠8000釋出了。

開啟網頁,在瀏覽器中輸入:http://localhost:8000/reverse/hiekaypython

介面上輸出什麼結果?

image.png

還可以在命令終端,用下面方式除錯,跟在網頁上輸出是等同效果。

$ curl http://localhost:8000/reverse/hiekaypython
nohtypyakeih

再看另外一個路徑,執行的是否是下面的結果呢?

$ curl http://localhost:8000/wrap -d text=I+love+Python+programming+and+I+am+writing+python+lessons+on+line
I love Python programming and I am
writing python lessons on line

除錯通過,就開始分析其中的奧妙。

get()

在ReverseHandler類中,定義了這個方法。

class ReverseHandler(tornado.web.RequestHandler):
    def get(self, input_word):
        self.write(input_word[::-1])

這個get()方法要和下面Application例項化中的路徑:

(r"/reverse/(w+)", ReverseHandler),

關聯起來看。

首先看路徑設定:r"/reverse/(w+)",這個路徑的意思就是可以在瀏覽器的url中輸入:http://localhost:8000/reverse/dddd,這個樣子的地址,注意路徑中的(w+),是正規表示式,在reverse/的後面可以輸入一個或者多個包括下劃線的任何單詞字元。也就是dddd可以更換為任何其它字母或者下劃線,一個或者多個均可以。%60%EF%BC%8C%E6%98%AF%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F%EF%BC%8C%E5%9C%A8reverse/%E7%9A%84%E5%90%8E%E9%9D%A2%E5%8F%AF%E4%BB%A5%E8%BE%93%E5%85%A5%E4%B8%80%E4%B8%AA%E6%88%96%E8%80%85%E5%A4%9A%E4%B8%AA%E5%8C%85%E6%8B%AC%E4%B8%8B%E5%88%92%E7%BA%BF%E7%9A%84%E4%BB%BB%E4%BD%95%E5%8D%95%E8%AF%8D%E5%AD%97%E7%AC%A6%E3%80%82%E4%B9%9F%E5%B0%B1%E6%98%AFdddd%E5%8F%AF%E4%BB%A5%E6%9B%B4%E6%8D%A2%E4%B8%BA%E4%BB%BB%E4%BD%95%E5%85%B6%E5%AE%83%E5%AD%97%E6%AF%8D%E6%88%96%E8%80%85%E4%B8%8B%E5%88%92%E7%BA%BF%EF%BC%8C%E4%B8%80%E4%B8%AA%E6%88%96%E8%80%85%E5%A4%9A%E4%B8%AA%E5%9D%87%E5%8F%AF%E4%BB%A5%E3%80%82)

在URL中輸入的這個地址,就被ReverseHandler類中的get()方法接收,這就是(r"/reverse/(w+)", ReverseHandler)之含義了。那麼,ReverseHandler中的get()方法如何接收url中傳遞過來的資訊呢?

前文已經說過,在def get(self, input_word)中,self引數在類例項化後對應的是tornado.web.RequestHandler,另外一個引數input_word用來接收來自url的資訊,但是它只接收所設定的路徑尾部資料,也就是路徑r"/reverse/(w+)"中reverse後面的第一個分割符號“/”之後的內容,都被input_word接收過來,即正規表示式的內容。

input_word接收過來的物件,是什麼型別呢?猜測一下,從前面程式的執行結果看,肯定是某種序列型別的物件。具體是哪種呢?可以實驗。

將get方法修改為:

def get(self, input_word):
    input_type = type(input_word)
    self.write("%s"%input_type)

再執行程式,列印出來的是:

$ curl http://localhost:8000/reverse/hiekay
<type `unicode`>

這說明,get()方法通過URL接收到的資料型別是unicode編碼的字元,即字串。

原來類方法中的self.write(input_word[::-1])含義是,將原來的字串倒置,並返回該資料到客戶端(如頁面)。

>>> a = "python,hiekay"
>>> a[::-1]
`yakeih,nohtyp`
>>> b = [1,2,3,4]
>>> b[::-1]
[4, 3, 2, 1]
>>> c = ("a","b","c")
>>> c[::-1]
(`c`, `b`, `a`)

這是一種將序列型別物件倒置的一種方法。

總結一下:get()通過第二個引數,獲得已經設定的顯示路徑中最後一個/後面的資料,並且為unincode編碼的字元。

這種方式通過URL得到有關資料,也就是說在URL中,只需要以http://localhost/aaa/bbb/ccc的形式來顯示路徑即可。是否注意到,有的網站是這麼顯示的:http://localhost/aaa?name=Tom&&?age=25,這其實是兩種不同的規範也好、方法也罷的東西,前者更接近時下流行的REST規範,可能聽說過MVC吧,我聽不少的公司都強調網站要符合MVC規範,殊不知,更流行的是REST了。那麼到底那個好呢?我的觀點:It depends.如果有興趣,請閱讀:《理解本真的REST架構風格》,對REST瞭解一二。

post()方法

post()也是web上常用的方法,在本例中,該方法寫在了WrapHandler類中:

class WrapHandler(tornado.web.RequestHandler):
    def post(self):
        text = self.get_argument("text")
        width = self.get_argument("width", 40)
        self.write(textwrap.fill(text, width))

對應的Application類路徑:

(r"/wrap", WrapHandler)

但是,要注意,post()無法從URL中獲得資料。這是跟get()方法非常不一樣的。關於get和post之間的區別,請點選《HTTP POST GET 本質區別詳解》閱讀。

客戶端的資料通過post方法傳送到伺服器,這個內在過程就是由所謂HTTP協議完成,不用去管它,因為現在我們只是研究應用層,不去深入網路協議的層面。

我要解釋的是,post()方法怎麼接收到客戶端傳過來的資料。

因為post不能從URL中得到資料,所以就不能用類似的方式在網頁的url中輸入要傳給它的資料了,只能這樣來測試:

$ curl http://localhost:8000/wrap -d text=I+love+Python+programming+and+I+am+writing+python+lessons+on+line
I love Python programming and I am
writing python lessons on line

請注意,URL依然是http://localhost:8000/wrap,後面的部分-d text=...,就是向這個地址對應的類WrapHandler中的post方法傳送相應的資料,這個資料被tornado.web.RequestHandler中的get_arugment()方法獲得,也就是通過text=self.get_argument("text")得到傳過來的物件,並賦值給text。

這裡需要提醒注意,self.get_argument("text")的引數中,是"text",就意味著,傳入資料的時候,需要用text這個變數,即必須寫成text=...。如果self.get_argument("word"),那麼就應該是word=...方式傳入資料了。

此時是否已經曉得,get_argument()在post方法中,能夠獲得客戶端傳過來的資料,當然是unicode編碼的。得到這個資料之後,就可以按照自己的需要進行操作了。

下一句width = self.get_argumen("width", 40)是要返回一個物件,這個物件約定變數為40,並將它用在下面的textwrap.fill(text, width)中。這裡並沒有什麼特別,也可以寫成width = 40,其實就是給textwrap.fill()提供引數罷了。關於textwrap模組中的fill方法,可以用help命令來看看。

>>> import textwrap
>>> help(textwrap.fill)

Help on function fill in module textwrap:

fill(text, width=70, **kwargs)
    Fill a single paragraph of text, returning a new string.

    Reformat the single paragraph in `text` to fit in lines of no more
    than `width` columns, and return a new string containing the entire
    wrapped paragraph.  As with wrap(), tabs are expanded and other
    whitespace characters converted to space.  See TextWrapper class for
    available keyword args to customize wrapping behaviour.

簡要總結RequestHandler

RequestHandler就是請求處理程式的方法,從上面的流程中,可以簡要地初步地認為(深奧的東西還不少,這裡只能是簡要地初步地膚淺地,隨著學習的深入會一點點深入地):

  • 通過self.write()向客戶端返回資料
  • get()中,以一個引數從URL路徑末尾獲取資料,特別提醒,這是在本講的例子中,get()方法中,用第二個引數獲得url資料。在上一講中,同樣是get()方法,用到了greeting = self.get_argument(`greeting`, `Hello`),於是不需要在get()中另外寫引數,只需要通過”greeting”就可以得到URL中的資料,不過這時候的url應該寫成http://localhost:8000/?greeting=PYTHON的樣式,於是字元傳`PYTHON`就能夠讓get()中的self.get_argument(`greeting`,`Hello`)獲得,如果沒有,就是`Hello`。
  • post()中,以self.argument("text")的形式得到text為標籤提交的資料。

get和post是http中用的最多的方法啦。此外,Tornado也還支援其它的HTTP請求,如:PUT、DELETE、HEAD、OPTIONS。在具體程式設計的時候,如果用到,可以搜尋,一般用的不多。

最後交代一句,get和post方法,由於一個是通過URL得到資料,另外一個不是,所以,他們可以寫到同一個類中,彼此互不干擾。


相關文章