1. 程式人生 > >淺析HTTP中POST和GET區別並用Python模擬其響應和請求

淺析HTTP中POST和GET區別並用Python模擬其響應和請求

    最近在幾周在做手遊崩潰資訊收集和上傳,拿到崩潰資訊後,使用的是HTTP的POST方法上傳到公司共用的伺服器的,因此做簡單總結。本文首先簡單介紹了HTTP協議,主要說明了POST方法和GET方法的區別;然後用Python實現了 對POST方法和GET方法的響應;最後用Python模擬了POST方法和GET方法的請求。

HTTP協議簡介

    HTTP是Hyper Text Transfer Protocol(超文字傳輸協議)的縮寫,簡單來說它是一個應用層的協議,它允許將超文字標記語言(HTML)文件從Web伺服器傳送到客戶端的瀏覽器。HTTP是一個無狀態的協議,即同一個客戶端的這次請求和上次請求是沒有對應關係,對http伺服器來說,它並不知道這兩個請求來自同一個客戶端,為了解決這個問題,Web程式引入了Cookie機制來維護狀態。
    HTTP協議通常基於TCP協議來實現的,有時也基於於TLS或SSL協議(這個兩個協議也是基於TCP協議來說實現)來實現,這個時候,就成了我們常說的HTTPS,每次HTTP操作都至少有下面幾個過程:首先客戶端與服務端建立連線;建立建立後,客戶端按照協議格式傳送請求;服務端接到請求後,同樣按照某個格式返回響應資料;最後客戶端與服務端斷開連線。
    通常我們開啟一個網頁,需要瀏覽器傳送多次請求,因為一個網頁中可能引用了其他檔案,比如圖片等檔案,這時候瀏覽器會自動再次傳送請求去獲取圖片等資料,直到網頁上的資料被完全顯示出來。

POST和GET區別

    HTTP協議定義了很多與伺服器互動的方法,最基本的有4種,分別是GET,POST,PUT,DELETE. 一個URL地址用於描述一個網路上的資源,而HTTP中的GET, POST, PUT, DELETE就對應著對這個資源的查,改,增,刪4個操作,其中最常見請求方式是GET和POST,並且現在瀏覽器一般只支援GET和POST方法。GET一般用於獲取/查詢資源資訊,而POST一般用於更新資源資訊,他們之間主要區別如下:
    1)根據HTTP規範,GET用於資訊獲取,而且應該是安全的和冪等的,這裡安全是指該操作用於獲取資訊而非修改資訊,冪等是指對同一URL的多個請求應該返回同樣的結果(這一點在實質實現時,可能並不滿足);
POST表示可能修改變伺服器上的資源的請求。
    2)GET請求的資料會附在URL之後(就是把資料放置在HTTP協議頭中),以?分割URL和傳輸資料,引數之間以&相連,如果資料是英文字母/數字,原樣傳送,如果是空格,轉換為+,如果是中文/其他字元,
則直接把字串用BASE64編碼;POST把提交的資料則放置在是HTTP包的包體中。
    3)因為GET是通過URL提交資料,那麼GET可提交的資料量就跟URL的長度有直接關係,理論上URL長度是沒有限制的,即HTTP協議沒有規定URL的長度,但在實質中,特定的瀏覽器可能對這個長度做了限制;理論上POST也是沒有大小限制的,HTTP協議規範也沒有進行大小限制,但在服務端通常會對這個大小做一個限制,當然這個限制比GET寬鬆的多,即使用POST可以提交的資料量比GET大得多。
    最後,網上有人說,POST的安全性要比GET的安全性高,實質上POST跟GET都是明文傳輸,這可以通過類似WireShark工具看到。總之,Get是向伺服器發索取資料的一種請求,而Post是向伺服器提交資料的一種請求。

POST和GET方法響應Python實現

    下面程式碼實現對POST方法和GET方法的響應:

#!/usr/bin/python
#coding=utf8
"""
import sys
reload(sys)
sys.setdefaultencoding('utf-8')
"""
from BaseHTTPServer import BaseHTTPRequestHandler,HTTPServer
from os import curdir, sep
import cgi
import logging
import time

PORT_NUMBER = 8080
RES_FILE_DIR = "."

class myHandler(BaseHTTPRequestHandler):
	
	def do_GET(self):
		if self.path=="/":
			self.path="/index_example3.html"

		try:
			#根據請求的副檔名,設定正確的mime型別
			sendReply = False
			if self.path.endswith(".html"):
				mimetype='text/html'
				sendReply = True
			if self.path.endswith(".jpg"):
				mimetype='image/jpg'
				sendReply = True
			if self.path.endswith(".gif"):
				mimetype='image/gif'
				sendReply = True
			if self.path.endswith(".js"):
				mimetype='application/javascript'
				sendReply = True
			if self.path.endswith(".css"):
				mimetype='text/css'
				sendReply = True

			if sendReply == True:
				#讀取相應的靜態資原始檔,併發送它
				f = open(curdir + sep + self.path, 'rb')
				self.send_response(200)
				self.send_header('Content-type',mimetype)
				self.end_headers()
				self.wfile.write(f.read())
				f.close()
			return

		except IOError:
			self.send_error(404,'File Not Found: %s' % self.path)

	def do_POST(self):
		logging.warning(self.headers)
		form = cgi.FieldStorage(
			fp=self.rfile,
			headers=self.headers,
			environ={'REQUEST_METHOD':'POST',
					'CONTENT_TYPE':self.headers['Content-Type'],
					})

		file_name = self.get_data_string()
		path_name = '%s/%s.log' % (RES_FILE_DIR,file_name)
		fwrite = open(path_name,'a')

		fwrite.write("name=%s\n" % form.getvalue("name",""))
		fwrite.write("addr=%s\n" % form.getvalue("addr",""))
		fwrite.close()

		self.send_response(200)
		self.end_headers()
		self.wfile.write("Thanks for you post")

	def get_data_string(self):
		now = time.time()
		clock_now = time.localtime(now)
		cur_time = list(clock_now)
		date_string = "%d-%d-%d-%d-%d-%d" % (cur_time[0],
				cur_time[1],cur_time[2],cur_time[3],cur_time[4],cur_time[5])
		return date_string		
try:
	server = HTTPServer(('', PORT_NUMBER), myHandler)
	print 'Started httpserver on port ' , PORT_NUMBER

	server.serve_forever()

except KeyboardInterrupt:
	print '^C received, shutting down the web server'
	server.socket.close()
    對於上面POST響應實現,值得一提的是,若客戶端傳送過來一個檔案,則方法getvalue()會把整個檔案內容讀入記憶體,這可能不是我們想要的,這時可以使用form的屬性file或filename,比如下面程式碼,計算上傳程式碼的行數:
fileitem = form["userfile"]
if fileitem.file:
    linecount = 0
    while 1:
        line = fileitem.file.readline()
        if not line: break
        linecount = linecount + 1
POST和GET方法請求Python實現
    下面程式碼實現了GET方法的請求:
#!/usr/bin/env python
#coding=utf8
 
import httplib
 
httpClient = None
 
try:
    httpClient = httplib.HTTPConnection('localhost', 8080, timeout=30)
    httpClient.request('GET', '/test0.html')
 
    #response是HTTPResponse物件
    response = httpClient.getresponse()
    print response.status
    print response.reason
    print response.read()
except Exception, e:
    print e
finally:
    if httpClient:
        httpClient.close()
    下面程式碼實現了POST方法的請求:
#!/usr/bin/env python
#coding=utf8
 
import httplib, urllib
 
httpClient = None
try:
    params = urllib.urlencode({'name': 'Maximus', 'addr': "GZ"})
    headers = {"Content-type": "application/x-www-form-urlencoded"
                    , "Accept": "text/plain"}
 
    httpClient = httplib.HTTPConnection("localhost", 8080, timeout=30)
    httpClient.request("POST", "/test0.html", params, headers)
 
    response = httpClient.getresponse()
    print response.status
    print response.reason
    print response.read()
    print response.getheaders() #獲取頭資訊
except Exception, e:
    print e
finally:
    if httpClient:
        httpClient.close()
參考資料

http://www.cnblogs.com/TankXiao/archive/2012/02/13/2342672.html
http://www.cnblogs.com/hyddd/archive/2009/03/31/1426026.html
http://www.acmesystems.it/python_httpserver
http://georgik.sinusgear.com/2011/01/07/how-to-dump-post-request-with-python/
http://www.01happy.com/python-httplib-get-and-post/