RESTful客戶端庫:RestClient
WebService
話說我剛知道這詞的時候還是十二年前…大約2001年,微軟的.net剛出來的時候,這貨就熱了,只不過當年這貨的標配通訊協議是SOAP。當年我覺得這貨還是很方便的,但是在嚐鮮過後,我根本沒有興趣將它用於實際的應用…它實在是太笨重了。
這種笨重包括幾個方面:首先SOAP本身的資料結構就很囉嗦,加上XML就更囉嗦了。其次,各種實現技術在高層邏輯上的定義各有一套,實際上並不那麼通用。最主要的是要用這貨,需要帶上太多的開發庫什麼的,而且用起來實在也沒有傳說中的那麼方便。
說實話,這十二年來,我基本上就沒見過像樣的應用——聽說過一些,也很少,評價普遍也不是很高。在web領域,它甚至未必有xml-rpc常見。
隨著RESTful和JSON的流行,新的WebService模型已經成為了事實標準,那就是以標準HTTP請求構建RESTful的API結構,以JSON定義交換協議的資料結構,組成了新一代的輕量級WebService模型。
就目前來說,提供RESTful API已經成了很多網站的標配…即使你不提供開放平臺服務,也要考慮到移動應用啊,沒有比這更好的實現方案了——技術足夠成熟(大網站都在用)、節約資源(大多數時候,這意味著效能更好)、簡單輕量(需要學習的東西不多,依賴的東西也不多)。
更詳細的理由可以參考一下CSDN的CTO範凱的這篇文章《Ruby社群應該去Rails化了》。
這其中最著名的應該是Facebook的開放平臺,而應用最廣泛的則是Twitter的API介面。國內的山寨版本就是像人人網、微博網之類的。
因為個人使用需要,我對Twitter的API有過一段時間研究,不過坦白說,Twitter的這套API定義得相當不夠RESTful,要學習如何定義API,還是強烈建議學習foursquare的API v2(當前版本),這才是規範的RESTful實現…不過它們的API經常不定期微調,所以需要一個v引數,這點略不規範。
RESTful客戶端
回到主題上,說了這麼多關於RESTful WebService的話題,本文主要還是要介紹RESTful客戶端的技術。
關於客戶端對RESTful WebService的呼叫,有很多方法,理論上只要是支援標準HTTP請求的方法都可以用。比如我前兩年在介紹OAuth 1.0a
但是對我來說,這還不夠方便。
用過SOAP的人都知道,SOAP有個好東西叫做WSDL,通過匯入這個東西,可以生成一個wrapper,使得遠端呼叫的使用像本地方法呼叫一樣簡單,對於靜態語言來說,還可以提供語法檢查。
但是RESTful WebService就沒有這種東西了,只有一份給人看的文件,還需要人工自己轉成程式碼——當然通常服務端也會有提供參考實現的庫,但總是不方便——因為每個服務商都有自己一套,現在API這麼多,實現風格又各異,學起來太麻煩,還容易搞錯。
要是再加上各種Authentication/Authorization,麻煩的事情就更多了。
於是我想到利用動態語言的動態性,對這些請求進行動態的wrapping,做了這個通用庫。
基本的RESTful請求
在談論這個庫的使用之前,先說一下基本的RESTful請求結構:
一般來說請求由這幾個主要部分組成:HTTP請求方法(GET,POST,PUT,DELETE...),URI,引數。
除此之外,還可能用到的內容有:HTTP請求頭,HTTP響應程式碼。
一個常見的例子類似這樣:
GET http://api.yourserver.com/v1/objects/function?id=xxxx
或者:
GET http://api.yourserver.com/v1/objects/xxxx/function
其中的xxxx就是id引數
對於支援多種資料格式的WebService(如早期的Twitter和現在的飯否,都支援除JSON以外的XML和RSS兩種格式),在URI上還會有格式字尾,如:
GET http://api.yourserver.com/v1/objects/xxxx/function.xml
而在如BasicAuth或Header方式的OAuth1.0a/2.0下,還需要操作HTTP請求頭。出錯的時候還需要處理HTTP響應程式碼。
這些都是不必要的麻煩。所以我做了這麼個庫試圖解決這些問題。
我的目標是把這類的請求包裝起來,通過標準的Python呼叫來實現。比如上面的請求可以簡化為這樣的Python程式碼:
api.object.GET_function(xxxx)
這樣就方便多了。
RestClient庫
這個庫就一個檔案,包括兩個方面:Auth和APIClient。
其中APIClient是一個wrapper,用於實現動態將python呼叫轉為RESTful請求。Auth部分包括了常用的BasicAuth, OAuth1.0a, OAuth2.0三種認證方式。
對於那麼多的API是不是需要對每個API去實現一個api類,對於每個API的那麼多功能,是不是需要實現一堆的objects類和無數的function方法呢?那樣不就跟服務端提供的庫一樣了嘛。
答案當然是不需要。
以飯否為例,實現一個飯否API只需要這麼幾行程式碼:
class Fanfou(APIClient):
def __init__(self, client_key, client_secret, callback="", access_token=""):
APIClient.__init__(self, AuthOAuth1("http://fanfou.com/oauth", client_key, client_secret, callback=callback, access_token=access_token),
"http://api.fanfou.com",
['search', 'blocks', 'users', 'account', 'saved_searches', 'photos', 'trends',
'followers', 'favorites', 'friendships', 'friends', 'statuses', 'direct_messages'],
"json")
關於其中的引數,簡單解釋一下:
父類APIClient需要五個引數:auth, api_url, object_list, postfix, ssl_verify
其中auth是一個auth物件,用來指定用哪種認證方式。api_url是指API請求的URL,即所有RESTful請求的URI最前面共同的那一部分。object_list是可選的,沒有這一項的話,可以使用任意名字的object,如果有這個list,則只能使用這裡列出的部分,建議提供這個引數,這樣可以不用等到服務端返回404才知道搞錯名字。postfix是格式字尾,如服務端不需要加字尾,這個引數可以不用。注意:沒有json和空以外的其它選擇,本庫不支援XML或RSS格式。ssl_verify用於SSL證書檢查,預設為True,但是因為requests似乎不是使用作業系統的證書體系,而是自己弄了一套,所以有些正常的SSL網站也會報SSL驗證錯誤,另外對一些使用自簽名證書的網站(理由你懂的),也需要將這個引數設定為False才不報錯。
這裡使用了OAuth1.0a,AuthOAuth1需要四個引數:auth_url, client_key, client_secret, callback, access_token。auth_url是請求認證的呼叫的根URL,中間二者來自於你的服務端所提供,callback是回撥你的客戶端時用的URL。具體參見OAuth 1.0a規範文件或我以前的文章說明。最後一個access_token則是對於預先儲存過access_token的情況下,可以直接使用,不用每次重新登入授權。
使用的方法也很簡單,關於OAuth 1.0a的請求部分原理參見我以前的文章,這裡不再重複,使用方法參見專案的demoweb.py這個web服務程式例子。
假設已經完成登入,取得了有效的access_token,可以這樣呼叫飯否API:
fan = Fanfou(client_key, client_secret, access_token=access_token) # 前兩個引數來自服務端所提供,最後一個引數來自登入後取得的access_token
user = fan.account.GET_verify_credentials() # 呼叫 GET http://api.fanfou.com/account/verify_credentials
# user 物件就是取得的呼叫結果
(不過因為某些腦子被槍打過的傢伙在領導著這個國家的網際網路,所以目前在中國大陸境內訪問 bitbucket.org 和 github.com 都不太順暢,丫們真是中國技術創新最大的敵人,真不知道這兩個分享原始碼的網站哪裡戳到丫們的G點了…)
目前我已經提供了飯否、Foursquare和Google(只包含登入功能)等的三個API包裝,再增加別的包裝也是很容易的事情。