1. 程式人生 > >Twisted的WEB開發

Twisted的WEB開發

關閉 沒有 user tpch eth 主體 版本 else 意義

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的不同處理,然後向客戶端寫入響應信息,並在最後調用關閉請求。步驟如下:

  1. 過濾URL, self.path
  2. self.write(data) 向客戶端寫入數據
  3. 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資源

  1. 每個資源都是 twisted.web.resource.Resource 的子類
  2. 可以自己定義構造函數
  3. 要重載 render(self,request) 方法來響應請求
  4. 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開發