Files
build-web-application-with-…/zh-tw/08.3.md
2019-02-26 01:40:54 +08:00

129 lines
7.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 8.3 REST
RESTful是目前最為流行的一種網際網路軟體架構。因為它結構清晰、符合標準、易於理解、擴充套件方便所以正得到越來越多網站的採用。本小節我們將來學習它到底是一種什麼樣的架構以及在Go裡面如何來實現它。
## 什麼是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的架構圖
![](images/8.3.rest2.png?raw=true)
圖8.5 REST架構圖
當REST架構的約束條件作為一個整體應用時將產生一個可以擴充套件到大量客戶端的應用程式。它還降低了客戶端和伺服器之間的互動延遲。統一介面簡化了整個系統架構改進了子系統之間互動的可見性。REST簡化了客戶端和伺服器的實現而且對於使用REST開發的應用程式更加容易擴充套件。
下圖展示了REST的擴充套件性
![](images/8.3.rest.png?raw=true)
圖8.6 REST的擴充套件性
## RESTful的實現
Go沒有為REST提供直接支援但是因為RESTful是基於HTTP協議實現的所以我們可以利用`net/http`套件來自己實現當然需要針對REST做一些改造REST是根據不同的method來處理相應的資源目前已經存在的很多自稱是REST的應用其實並沒有真正的實現REST我暫且把這些應用根據實現的method分成幾個級別請看下圖
![](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的應用設計。
```Go
package main
import (
"fmt"
"log"
"net/http"
"github.com/julienschmidt/httprouter"
)
func Index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
fmt.Fprint(w, "Welcome!\n")
}
func Hello(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
fmt.Fprintf(w, "hello, %s!\n", ps.ByName("name"))
}
func getuser(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
uid := ps.ByName("uid")
fmt.Fprintf(w, "you are get user %s", uid)
}
func modifyuser(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
uid := ps.ByName("uid")
fmt.Fprintf(w, "you are modify user %s", uid)
}
func deleteuser(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
uid := ps.ByName("uid")
fmt.Fprintf(w, "you are delete user %s", uid)
}
func adduser(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
// uid := r.FormValue("uid")
uid := ps.ByName("uid")
fmt.Fprintf(w, "you are add user %s", uid)
}
func main() {
router := httprouter.New()
router.GET("/", Index)
router.GET("/hello/:name", Hello)
router.GET("/user/:uid", getuser)
router.POST("/adduser/:uid", adduser)
router.DELETE("/deluser/:uid", deleteuser)
router.PUT("/moduser/:uid", modifyuser)
log.Fatal(http.ListenAndServe(":8080", router))
}
```
上面的程式碼示範瞭如何編寫一個REST的應用我們訪問的資源是使用者我們透過不同的method來訪問不同的函式這裡使用了第三方函式庫`github.com/julienschmidt/httprouter`在前面章節我們介紹過如何實現自訂的路由器這個函式庫實現了自訂路由和方便的路由規則對映透過它我們可以很方便的實現REST的架構。透過上面的程式碼可知REST就是根據不同的method訪問同一個資源的時候實現不同的邏輯處理。
## 總結
REST是一種架構風格汲取了WWW的成功經驗無狀態以資源為中心充分利用HTTP協議和URI協議提供統一的介面定義使得它作為一種設計Web服務的方法而變得流行。在某種意義上透過強調URI和HTTP等早期Internet標準REST是對大型應用程式伺服器時代之前的Web方式的迴歸。目前Go對於REST的支援還是很簡單的透過實現自訂的路由規則我們就可以為不同的method實現不同的handle這樣就實現了REST的架構。
## links
* [目錄](<preface.md>)
* 上一節: [WebSocket](<08.2.md>)
* 下一節: [RPC](<08.4.md>)