RESTful,是目前最為流行的一種互聯網軟件架構。因為它結構清晰、符合標準、易于理解、擴展方便,所以正得到越來越多網站的采用。本小節我們將來學習它到底是一種什么樣的架構?以及在Go里面如何來實現它。
## [](https://github.com/astaxie/build-web-application-with-golang/blob/master/zh/08.3.md#什么是rest)什么是REST
REST(REpresentational State Transfer)這個概念,首次出現是在 2000年Roy Thomas Fielding(他是HTTP規范的主要編寫者之一)的博士論文中,它指的是一組架構約束條件和原則。滿足這些約束條件和原則的應用程序或設計就是RESTful的。
要理解什么是REST,我們需要理解下面幾個概念:
* 資源(Resources) REST是"表現層狀態轉化",其實它省略了主語。"表現層"其實指的是"資源"的"表現層"。
那么什么是資源呢?就是我們平常上網訪問的一張圖片、一個文檔、一個視頻等。這些資源我們通過URI來定位,也就是一個URI表示一個資源。
* 表現層(Representation)
資源是做一個具體的實體信息,他可以有多種的展現方式。而把實體展現出來就是表現層,例如一個txt文本信息,他可以輸出成html、json、xml等格式,一個圖片他可以jpg、png等方式展現,這個就是表現層的意思。
URI確定一個資源,但是如何確定它的具體表現形式呢?應該在HTTP請求的頭信息中用Accept和Content-Type字段指定,這兩個字段才是對"表現層"的描述。
* 狀態轉化(State Transfer)
訪問一個網站,就代表了客戶端和服務器的一個互動過程。在這個過程中,肯定涉及到數據和狀態的變化。而HTTP協議是無狀態的,那么這些狀態肯定保存在服務器端,所以如果客戶端想要通知服務器端改變數據和狀態的變化,肯定要通過某種方式來通知它。
客戶端能通知服務器端的手段,只能是HTTP協議。具體來說,就是HTTP協議里面,四個表示操作方式的動詞:GET、POST、PUT、DELETE。它們分別對應四種基本操作:GET用來獲取資源,POST用來新建資源(也可以用于更新資源),PUT用來更新資源,DELETE用來刪除資源。
綜合上面的解釋,我們總結一下什么是RESTful架構:
* (1)每一個URI代表一種資源;
* (2)客戶端和服務器之間,傳遞這種資源的某種表現層;
* (3)客戶端通過四個HTTP動詞,對服務器端資源進行操作,實現"表現層狀態轉化"。
Web應用要滿足REST最重要的原則是:客戶端和服務器之間的交互在請求之間是無狀態的,即從客戶端到服務器的每個請求都必須包含理解請求所必需的信息。如果服務器在請求之間的任何時間點重啟,客戶端不會得到通知。此外此請求可以由任何可用服務器回答,這十分適合云計算之類的環境。因為是無狀態的,所以客戶端可以緩存數據以改進性能。
另一個重要的REST原則是系統分層,這表示組件無法了解除了與它直接交互的層次以外的組件。通過將系統知識限制在單個層,可以限制整個系統的復雜性,從而促進了底層的獨立性。
下圖即是REST的架構圖:
[](https://github.com/astaxie/build-web-application-with-golang/blob/master/zh/images/8.3.rest2.png?raw=true)
圖8.5 REST架構圖
當REST架構的約束條件作為一個整體應用時,將生成一個可以擴展到大量客戶端的應用程序。它還降低了客戶端和服務器之間的交互延遲。統一界面簡化了整個系統架構,改進了子系統之間交互的可見性。REST簡化了客戶端和服務器的實現,而且對于使用REST開發的應用程序更加容易擴展。
下圖展示了REST的擴展性:
[](https://github.com/astaxie/build-web-application-with-golang/blob/master/zh/images/8.3.rest.png?raw=true)
圖8.6 REST的擴展性
## [](https://github.com/astaxie/build-web-application-with-golang/blob/master/zh/08.3.md#restful的實現)RESTful的實現
Go沒有為REST提供直接支持,但是因為RESTful是基于HTTP協議實現的,所以我們可以利用`net/http`包來自己實現,當然需要針對REST做一些改造,REST是根據不同的method來處理相應的資源,目前已經存在的很多自稱是REST的應用,其實并沒有真正的實現REST,我暫且把這些應用根據實現的method分成幾個級別,請看下圖:
[](https://github.com/astaxie/build-web-application-with-golang/blob/master/zh/images/8.3.rest3.png?raw=true)
圖8.7 REST的level分級
上圖展示了我們目前實現REST的三個level,我們在應用開發的時候也不一定全部按照RESTful的規則全部實現他的方式,因為有些時候完全按照RESTful的方式未必是可行的,RESTful服務充分利用每一個HTTP方法,包括`DELETE`和`PUT`。可有時,HTTP客戶端只能發出`GET`和`POST`請求:
* HTML標準只能通過鏈接和表單支持`GET`和`POST`。在沒有Ajax支持的網頁瀏覽器中不能發出`PUT`或`DELETE`命令
* 有些防火墻會擋住HTTP?`PUT`和`DELETE`請求要繞過這個限制,客戶端需要把實際的`PUT`和`DELETE`請求通過 POST 請求穿透過來。RESTful 服務則要負責在收到的 POST 請求中找到原始的 HTTP 方法并還原。
我們現在可以通過`POST`里面增加隱藏字段`_method`這種方式可以來模擬`PUT`、`DELETE`等方式,但是服務器端需要做轉換。我現在的項目里面就按照這種方式來做的REST接口。當然Go語言里面完全按照RESTful來實現是很容易的,我們通過下面的例子來說明如何實現RESTful的應用設計。
~~~
package main
import (
"fmt"
"github.com/drone/routes"
"net/http"
)
func getuser(w http.ResponseWriter, r *http.Request) {
params := r.URL.Query()
uid := params.Get(":uid")
fmt.Fprintf(w, "you are get user %s", uid)
}
func modifyuser(w http.ResponseWriter, r *http.Request) {
params := r.URL.Query()
uid := params.Get(":uid")
fmt.Fprintf(w, "you are modify user %s", uid)
}
func deleteuser(w http.ResponseWriter, r *http.Request) {
params := r.URL.Query()
uid := params.Get(":uid")
fmt.Fprintf(w, "you are delete user %s", uid)
}
func adduser(w http.ResponseWriter, r *http.Request) {
params := r.URL.Query()
uid := params.Get(":uid")
fmt.Fprint(w, "you are add user %s", uid)
}
func main() {
mux := routes.New()
mux.Get("/user/:uid", getuser)
mux.Post("/user/:uid", modifyuser)
mux.Del("/user/:uid", deleteuser)
mux.Put("/user/", adduser)
http.Handle("/", mux)
http.ListenAndServe(":8088", nil)
}
~~~
上面的代碼演示了如何編寫一個REST的應用,我們訪問的資源是用戶,我們通過不同的method來訪問不同的函數,這里使用了第三方庫`github.com/drone/routes`,在前面章節我們介紹過如何實現自定義的路由器,這個庫實現了自定義路由和方便的路由規則映射,通過它,我們可以很方便的實現REST的架構。通過上面的代碼可知,REST就是根據不同的method訪問同一個資源的時候實現不同的邏輯處理。
## [](https://github.com/astaxie/build-web-application-with-golang/blob/master/zh/08.3.md#總結)總結
REST是一種架構風格,汲取了WWW的成功經驗:無狀態,以資源為中心,充分利用HTTP協議和URI協議,提供統一的接口定義,使得它作為一種設計Web服務的方法而變得流行。在某種意義上,通過強調URI和HTTP等早期Internet標準,REST是對大型應用程序服務器時代之前的Web方式的回歸。目前Go對于REST的支持還是很簡單的,通過實現自定義的路由規則,我們就可以為不同的method實現不同的handle,這樣就實現了REST的架構。
- 第一章 Go環境配置
- 1.1 Go安裝
- 1.2 GOPATH 與工作空間
- 1.3 Go 命令
- 1.4 Go開發工具
- 1.5 小結
- 第二章 Go語言基礎
- 2.1 你好,Go
- 2.2 Go基礎
- 2.3 流程和函數
- 2.4 struct類型
- 2.5 面向對象
- 2.6 interface
- 2.7 并發
- 2.8 總結
- 第三章 Web基礎
- 3.1 Web工作方式
- 3.2 Go搭建一個Web服務器
- 3.3 Go如何使得Web工作
- 3.4 Go的http包詳解
- 3.5 小結
- 第四章 表單
- 4.1 處理表單的輸入
- 4.2 驗證表單的輸入
- 4.3 預防跨站腳本
- 4.4 防止多次遞交表單
- 4.5 處理文件上傳
- 4.6 小結
- 第五章 訪問數據庫
- 5.1 database/sql接口
- 5.2 使用MySQL數據庫
- 5.3 使用SQLite數據庫
- 5.4 使用PostgreSQL數據庫
- 5.5 使用beedb庫進行ORM開發
- 5.6 NOSQL數據庫操作
- 5.7 小結
- 第六章 session和數據存儲
- 6.1 session和cookie
- 6.2 Go如何使用session
- 6.3 session存儲
- 6.4 預防session劫持
- 6.5 小結
- 第七章 文本處理
- 7.1 XML處理
- 7.2 JSON處理
- 7.3 正則處理
- 7.4 模板處理
- 7.5 文件操作
- 7.6 字符串處理
- 7.7 小結
- 第八章 Web服務
- 8.1 Socket編程
- 8.2 WebSocket
- 8.3 REST
- 8.4 RPC
- 8.5 小結
- 第九章 安全與加密
- 9.1 預防CSRF攻擊
- 9.2 確保輸入過濾
- 9.3 避免XSS攻擊
- 9.4 避免SQL注入
- 9.5 存儲密碼
- 9.6 加密和解密數據
- 9.7 小結
- 第十章 國際化和本地化
- 10.1 設置默認地區
- 10.2 本地化資源
- 10.3 國際化站點
- 10.4 小結
- 第十一章 錯誤處理,調試和測試
- 11.1 錯誤處理
- 11.2 使用GDB調試
- 11.3 Go怎么寫測試用例
- 11.4 小結
- 第十二章 部署與維護
- 12.1 應用日志
- 12.2 網站錯誤處理
- 12.3 應用部署
- 12.4 備份和恢復
- 12.5 小結
- 第十三章 如何設計一個Web框架
- 13.1 項目規劃
- 13.2 自定義路由器設計
- 13.3 controller設計
- 13.4 日志和配置設計
- 13.5 實現博客的增刪改
- 13.6 小結
- 第十四章 擴展Web框架
- 14.1 靜態文件支持
- 14.2 Session支持
- 14.3 表單及驗證支持
- 14.4 用戶認證
- 14.5 多語言支持
- 14.6 pprof支持
- 14.7 小結
- 附錄A 參考資料