Twisted的WEB開發
1 簡介
在WEB開發中,偶爾需要對HTTP協議更多底層細節進行控制,這時的django/web.py等等顯然無法滿足要求,所以只好求助於Twisted了。使用Twisted進行WEB開發,其實更合適的叫法應該是基於HTTP服務器的開發,因為Twisted相對底層,所以可以控制的東西也比較底層。
在Twisted的技術體系中,這個WEB開發實際上要涉及到HTTPChannel、HTTPFactory、Request三個層次的開發,以下詳訴。
HTTP協議參考 RFC2616 。
2 Twisted技術體系
Twisted技術體系包含2個層次:協議和工廠。協議負責連接成功以後對交互的處理,而工廠則是負責連接過程。在HTTP協議中,連接之後還有個生成HTTP請求報文的過程,所以構造出了一個Request對象來處理具體的一個HTTP請求的報文。
在HTTP中的請求報文處理對象是 twisted.web.http.Request 類;HTTP的協議類是 twisted.web.http.HTTPChannel ;HTTP工廠是 twisted.web.http.HTTPFactory 。
3 一個簡單的例子
節選自《Twisted網絡編程必備》:
from twisted.web import http class MyRequestHandler(http.Request): pages={ ‘/‘:‘<h1>Home</h1>Home Page‘, ‘/test‘:‘<h1>Test</h1>Test Page‘, } def process(self): if self.pages.has_key(self.path): self.write(self.pages[self.path]) else: self.setResponseCode(http.NOT_FOUND) self.write("<h1>Not Found</h1>Sorry, no such page.") self.finish() class MyHttp(http.HTTPChannel): requestFactory=MyRequestHandler class MyHttpFactory(http.HTTPFactory): protocol=MyHttp if __name__=="__main__": from twisted.internet import reactor reactor.listenTCP(8000,MyHttpFactory()) reactor.run()
與其他很多框架不同,TwistedWEB只有一個核心的請求處理類Request,各個針對不同的URL的請求也要通過這裏來分發。而這個類只要重載process() 方法就可以了,期間的很多數據都可以通過self來引用。
請求的處理流程也就是判斷對不同URL的不同處理,然後向客戶端寫入響應信息,並在最後調用關閉請求。步驟如下:
- 過濾URL, self.path
- self.write(data) 向客戶端寫入數據
- self.finish() 關閉響應
4 Twisted WEB Request參考
來自分析 twisted.web.http.http.py 源代碼。
4.1 請求
包含請求的數據,這裏都是指Request類的成員。
channel :包含上級的HTTP協議對象。
transport :通信對象。
method :HTTP方法,如GET和POST。
uri :全部請求的URI。
path :具體的請求路徑,不含參數。
args :請求參數,包括URL參數和POST參數。格式如 {‘key‘:[‘val1‘,‘val2‘],} 。
received_headers :請求報文的頭字段。
received_cookies :請求報文的cookie。
content :請求報文的實體主體,文件對象。
clientproto :發出請求的客戶端的HTTP版本。
client :?
host :?
getHeader(key) :獲取請求的頭字段。
getCookie(key) :獲取請求的cookie。
getAllHeaders() :所有請求的頭字段字典,就是返回received_headers。
getRequestHostname() :請求的host字段,不含端口號。
getHost() :原始請求的通信地址,返回host。
getClientIP() :獲取客戶端IP。
getUser() :獲取basic驗證中的用戶名。
getPassword() :獲取basic驗證中的密碼。
getClient() :?
4.2 響應
包含響應的數據,這裏都是Request類的成員。
headers :字典,包含響應報文的頭字段。
cookies :字典,包含響應報文的cookie。
finish() :結束響應報文。
write(data) :向客戶端發送數據,經過了HTTP包裝了。
addCookie(k,v,expires=None,domain=None,path=None,max_age=None,comment=None,secure=None):為響應報文添加一個cookie。
setResponseCode(code,message=None) :設置響應代碼,code參考常量定義。
setHeader(k,v) :設置頭字段。
redirect(url) :HTTP重定向。
setLastModified(when) :設置緩存超時,when的值為長整型的那個時間。
setETag(etag) :設置緩存標誌,用於在內容更改時讓用戶有所發覺。
setHost(host,port,ssl=0) :設置請求地址。用於代理服務器的重定向。
4.3 常量
沒有響應主體的code:
NO_BODY_CODES=(204,304)
responses=RESPONSES :字典,保存了各個響應碼的對應提示信息。
響應報文中的響應碼:
OK=200 :請求處理成功,最常見的響應代碼,但是正因為常見,所以默認就是這個了,也無須設置到setResponseCode。
NOT_MODIFIED=304 :請求的資源沒有沒有修改過,用於瀏覽器緩存。
BAD_REQUEST=400 :請求報文有語法錯誤。
UNAUTHORIZED=401 :尚未認證,要求用戶輸入認證信息。
FORBIDDEN=403 :禁止訪問。
NOT_FOUND=404 :請求的資源不存在。
INTERNAL_SERVER_ERROR=500 :服務器內部錯誤。
NOT_IMPLEMENTED=501 :該功能尚未實現。
BAD_GATEWAY=502 :請求路徑錯誤。
4.4 HTTPChannel
構造函數無參數,處理HTTP報文。
requestFactory=Request :指定了請求報文處理工廠。
4.5 HTTPFactory
__ini__(logPath=None,timeout=60*60*12) :構造函數可以設置日誌和超時。
buildProtocol(addr) :內部的構造協議對象的方法,不要調用。
protocol=HTTPChannel :設置協議對象。
5 比較完善的開發模式
建立一個Request類的子類作為請求工廠,或者說請求發布器,其中有識別不同的URL並的能力,通過字典找到該URL對應的函數,調用這個函數並傳遞self參數。每個具體的請求處理函數也只有1個request參數,返回數據都是直接寫入request.write()中。
一般來說請求工廠的process()中需要設置響應類型,如網頁的:
self.setHeader("Content-Type","text/html; charset=GB2312")
同時也要在沒有對應的URL時告知客戶端找不到:
self.setResponseCode(http.NOT_FOUND) self.write("<h1>Not Found</h1>Sorry, no such page.") self.finish()
至於self.finish()放在各個請求處理函數中還是放在process(),就是個人愛好問題了。比較推薦放在process()中。
提取請求參數的重點在request.args字典,每個鍵都是映射到一個列表,為了適應HTTP提交中一個鍵對應多個值的情況,當然,你也可以只取第一個值。
6 以resource方式提供WEB資源
- 每個資源都是 twisted.web.resource.Resource 的子類
- 可以自己定義構造函數
- 要重載 render(self,request) 方法來響應請求
- render 方法中的request對象實際就是Request的實例
一個例子:
from twisted.web import resource,static,server class HomePage(resource.Resource): def render(self,request): request.write("Home Page") return def getChild(self,path,request): return AnotherPage() #另外一個Resource的子類 if __name__=="__main__": from twisted.internet import reactor root=resource.Resource() root.putChild(‘‘,HomePage()) root.putChild(‘color‘,ColorRoot()) root.putChild(‘style.css‘,static.File(‘style.css‘)) site=server.Site(root) reactor.listenTCP(8000,site) reactor.run()
可以通過各個Resource的構造參數傳入path參數,用以後來尋找下級Resource的參數。
Note
關於Resource還有很多細節,但是對本文意義不大,所以略。
7 總結
總的來說,用Twisted來開發更適合於開發個框架,而不是直接做WEB應用,有如一直都很少有人直接用 mod_python 來開發WEB應用一樣。
Twisted的WEB開發