莫笑我老土,因為我確實是最近才聽說REST風(fēng)格的,以前就是覺得 /category/product/pid
這樣的地址非常的漂亮,但是那只是表象罷了,了解深入以后,發(fā)現(xiàn)必須有一個客戶端的Ajax Engine和Server端的服務(wù)配合,才能實現(xiàn)一個REST風(fēng)格的應(yīng)用,下面就是我的實驗。
問題?
要對外提供哪些服務(wù)。服務(wù)器端的服務(wù)可能會被眾多的瀏覽器請求,也可能被第三方應(yīng)用程序所調(diào)用,所以需要從總體上來考慮這個對外的“應(yīng)用程序接口(API),盡量保持接口的穩(wěn)定性。REST是一種風(fēng)格,并且形成了自己的規(guī)則,構(gòu)建這樣的應(yīng)用,應(yīng)盡量遵循REST的原則。
以一個足球服務(wù)為例,眾多的觀眾會要求觀看比賽的記錄,上傳新比賽記錄,更新比賽記錄,更正現(xiàn)有的比賽或者刪除比賽等等。像這樣描述的話,我們需要提供眾多不同的服務(wù),并且最終會倒在維護一致性的工作上。那么應(yīng)該怎么做呢,考慮一下客戶可能的請求方式:
GET方式請求一個新建比賽服務(wù):
http://example.com/newMatch?id=995&bluebaggers=150&redlegs=60
POST或PUT方式請求一個新建比賽服務(wù):
附加的XML為:
<match id="995">
<score team="bluebaggers">150</score>
<score team="redlegs">60</score>
</match>
CGI風(fēng)格的POST或PUT請求:
請求體:
id=995&bluebaggers=150&redlegs=60
或者一個維護服務(wù)的GET請求:
http://example.com/matchMaintenance/command=newMatch&id=995&bluebaggers=150&redlegs=60
或者POST請求
http://example.com/matchMaintenance/command=newMatch
<match id="995"><score team="bluebaggers">150</score><score team="redlegs">60</score></match>
以此類推,可以有很多這樣的功能。有些人覺得這并不是什么問題,對越來越多的請求,我們只要建立服務(wù),然后給出相應(yīng)的說明就可以了。但是,他還是存在缺點的。
也許我們會假設(shè)訪問只是來自腳本,那么這種情況可能會簡單一點。但實際上,還有很多的因素會涉及到,例如網(wǎng)頁瀏覽器(會存在后撤和刷新按鈕的問題)、Web服務(wù)器(可能會有緩存和編譯問題)、網(wǎng)絡(luò)路由和緩存問題、應(yīng)對爬蟲的騷擾、一些個人站點對網(wǎng)站內(nèi)容的抓取。如果我們考慮這些不同的請求,我們的程序就可以表現(xiàn)的更健壯。
理想的情況下,一個服務(wù)應(yīng)該有自我說明的能力。如果一個服務(wù)建立在一種約定俗成的條件下,那么大家就很容易適應(yīng)并且進行后續(xù)的開發(fā)。
REST就是考慮了這些因素,可以使用RESTful API來實現(xiàn)上面的服務(wù)。
RESTful 原則介紹
REST的主要原則有:
用URL表示資源。資源就像商業(yè)實體一樣,是我們希望作為API實體呈現(xiàn)的一部分。通常是一個名詞,每個資源都用一個獨一無二的URL來表示。
HTTP方法表示操作。REST充分利用了HTTP的方法,特別是GET、POST、PUT和DELETE。注意XMLHttpRequest對象實現(xiàn)了全部的方法,具體可以參看W3C HTTP 1.1 Specification。
也就是說,客戶端的任何請求都包含一個URL和一個HTTP方法?;氐缴厦娴睦又?,比賽顯然是一個實體,那么對于一個特定比賽的請求就表示為:
http://example.com/matches/995
這種方式是清晰明了的,也許和精確命名的方式有所區(qū)別,但是只要遵循這種形式,我們就能很快的進行GET、DELETE、UPDATE和新建操作。
RESTful的原則:
1、URL表示資源
2、HTTP方法表示操作
3、GET只是用來請求操作,GET操作永遠都不應(yīng)該修改服務(wù)器的狀態(tài)。但是這個也要具體情況進行分析,例如一個頁面中的計數(shù)器,每次訪問的時候確實引起了服務(wù)器數(shù)據(jù)的改變,但是在商業(yè)上來說,這并不是一個很重要的改變,所以仍然可以接收使用GET的方式來修改數(shù)據(jù)。
一個案例,使用GET方式修改數(shù)據(jù)遭受損失的案例
Witness the the debacle caused by the Google Accelerator interacting with non-RESTful services in mid-2005. The accelerator jumps ahead of the user and prefetches each link in case they should click on it (a non-Ajaxian example of Predictive Fetch). The problem came when users logged into non-RESTful applications likeBackpack. Because Backpack deletes items using GET calls, the accelerator - in its eagerness to activate each GET query - ended up deleting personal data. This could happen with regular search engine crawlers too, though the issue doesn't tend to come up because they don't have access to personal accounts.
4、服務(wù)應(yīng)該是無狀態(tài)的
在有狀態(tài)的會話中,服務(wù)器可以記錄之前的信息。而RESTful風(fēng)格中是不應(yīng)該讓服務(wù)器記錄狀態(tài)的,只有這樣服務(wù)器才具備可擴展性。當然,我們可以在客戶端使用cookie,而且只能用在客戶端向服務(wù)器發(fā)送請求的時候。
5、服務(wù)應(yīng)當是“冪等”的
“冪等”表示可以發(fā)送消息給服務(wù),然后可以再次毫不費力的發(fā)送同樣的消息給服務(wù)。例如,發(fā)送一個“刪除第995場比賽”的消息,可以發(fā)送一次,也可以連續(xù)發(fā)送十次,最后的結(jié)果都會保持一致。當然,RESTful的GET請求通常是冪等的,因為基本上不會改變服務(wù)器的狀態(tài)。注意:POST請求不能被定義為“冪等”,特別是在創(chuàng)建新資源的時候,一次請求創(chuàng)建一個資源,多次請求會創(chuàng)建多個資源。
6、擁抱超鏈接
7、服務(wù)應(yīng)當自我說明
例如 http://example.com/match/995 請求了一個具體的比賽,但是 http://example.com/match 并沒有對任何實體進行請求,因此,應(yīng)當返回一些介紹信息。
8、服務(wù)約束數(shù)據(jù)格式。數(shù)據(jù)必須符合要求的格式
在PHP的程序中,想要實現(xiàn)這種REST風(fēng)格的URL,僅僅依靠程序是不行的,還需要在服務(wù)器端配置rewrite規(guī)則,例如,對于一個REST風(fēng)格的資源請求:
http://www.api.com/product/113
一般實現(xiàn)的腳本為
http://www.api.com/product.php?id=113
這個是基于QueryString的,也可以做一個統(tǒng)一的 index. php 入口,然后通過處理URI的方式實現(xiàn),例如:
http://www.api.com/index.php/product/113
這樣的URL,都可以通過rewrite來實現(xiàn)rest風(fēng)格。總之,REST是一種程序設(shè)計的風(fēng)格,為我們整理自己的應(yīng)用設(shè)計提供了一個原則,在利用這些原則帶來的遍歷的同時,可以根據(jù)實際情況進行靈活的處理。
參考資料: