Go REST

2022-05-13 17:42 更新

RESTful,是目前最為流行的一種互聯(lián)網(wǎng)軟件架構(gòu)。因?yàn)樗Y(jié)構(gòu)清晰、符合標(biāo)準(zhǔn)、易于理解、擴(kuò)展方便,所以正得到越來(lái)越多網(wǎng)站的采用。本小節(jié)我們將來(lái)學(xué)習(xí)它到底是一種什么樣的架構(gòu)?以及在Go里面如何來(lái)實(shí)現(xiàn)它。

什么是REST

REST(REpresentational State Transfer)這個(gè)概念,首次出現(xiàn)是在 2000年Roy Thomas Fielding(他是HTTP規(guī)范的主要編寫者之一)的博士論文中,它指的是一組架構(gòu)約束條件和原則。滿足這些約束條件和原則的應(yīng)用程序或設(shè)計(jì)就是RESTful的。

要理解什么是REST,我們需要理解下面幾個(gè)概念:

  • 資源(Resources) REST是"表現(xiàn)層狀態(tài)轉(zhuǎn)化",其實(shí)它省略了主語(yǔ)。"表現(xiàn)層"其實(shí)指的是"資源"的"表現(xiàn)層"。

    那么什么是資源呢?就是我們平常上網(wǎng)訪問(wèn)的一張圖片、一個(gè)文檔、一個(gè)視頻等。這些資源我們通過(guò)URI來(lái)定位,也就是一個(gè)URI表示一個(gè)資源。

  • 表現(xiàn)層(Representation)

    資源是做一個(gè)具體的實(shí)體信息,他可以有多種的展現(xiàn)方式。而把實(shí)體展現(xiàn)出來(lái)就是表現(xiàn)層,例如一個(gè)txt文本信息,他可以輸出成html、json、xml等格式,一個(gè)圖片他可以jpg、png等方式展現(xiàn),這個(gè)就是表現(xiàn)層的意思。

    URI確定一個(gè)資源,但是如何確定它的具體表現(xiàn)形式呢?應(yīng)該在HTTP請(qǐng)求的頭信息中用Accept和Content-Type字段指定,這兩個(gè)字段才是對(duì)"表現(xiàn)層"的描述。

  • 狀態(tài)轉(zhuǎn)化(State Transfer)

    訪問(wèn)一個(gè)網(wǎng)站,就代表了客戶端和服務(wù)器的一個(gè)互動(dòng)過(guò)程。在這個(gè)過(guò)程中,肯定涉及到數(shù)據(jù)和狀態(tài)的變化。而HTTP協(xié)議是無(wú)狀態(tài)的,那么這些狀態(tài)肯定保存在服務(wù)器端,所以如果客戶端想要通知服務(wù)器端改變數(shù)據(jù)和狀態(tài)的變化,肯定要通過(guò)某種方式來(lái)通知它。

    客戶端能通知服務(wù)器端的手段,只能是HTTP協(xié)議。具體來(lái)說(shuō),就是HTTP協(xié)議里面,四個(gè)表示操作方式的動(dòng)詞:GET、POST、PUT、DELETE。它們分別對(duì)應(yīng)四種基本操作:GET用來(lái)獲取資源,POST用來(lái)新建資源(也可以用于更新資源),PUT用來(lái)更新資源,DELETE用來(lái)刪除資源。

綜合上面的解釋,我們總結(jié)一下什么是RESTful架構(gòu):

  • (1)每一個(gè)URI代表一種資源;
  • (2)客戶端和服務(wù)器之間,傳遞這種資源的某種表現(xiàn)層;
  • (3)客戶端通過(guò)四個(gè)HTTP動(dòng)詞,對(duì)服務(wù)器端資源進(jìn)行操作,實(shí)現(xiàn)"表現(xiàn)層狀態(tài)轉(zhuǎn)化"。

Web應(yīng)用要滿足REST最重要的原則是:客戶端和服務(wù)器之間的交互在請(qǐng)求之間是無(wú)狀態(tài)的,即從客戶端到服務(wù)器的每個(gè)請(qǐng)求都必須包含理解請(qǐng)求所必需的信息。如果服務(wù)器在請(qǐng)求之間的任何時(shí)間點(diǎn)重啟,客戶端不會(huì)得到通知。此外此請(qǐng)求可以由任何可用服務(wù)器回答,這十分適合云計(jì)算之類的環(huán)境。因?yàn)槭菬o(wú)狀態(tài)的,所以客戶端可以緩存數(shù)據(jù)以改進(jìn)性能。

另一個(gè)重要的REST原則是系統(tǒng)分層,這表示組件無(wú)法了解除了與它直接交互的層次以外的組件。通過(guò)將系統(tǒng)知識(shí)限制在單個(gè)層,可以限制整個(gè)系統(tǒng)的復(fù)雜性,從而促進(jìn)了底層的獨(dú)立性。

下圖即是REST的架構(gòu)圖:

當(dāng)REST架構(gòu)的約束條件作為一個(gè)整體應(yīng)用時(shí),將生成一個(gè)可以擴(kuò)展到大量客戶端的應(yīng)用程序。它還降低了客戶端和服務(wù)器之間的交互延遲。統(tǒng)一界面簡(jiǎn)化了整個(gè)系統(tǒng)架構(gòu),改進(jìn)了子系統(tǒng)之間交互的可見(jiàn)性。REST簡(jiǎn)化了客戶端和服務(wù)器的實(shí)現(xiàn),而且對(duì)于使用REST開(kāi)發(fā)的應(yīng)用程序更加容易擴(kuò)展。

下圖展示了REST的擴(kuò)展性:



RESTful的實(shí)現(xiàn)

Go沒(méi)有為REST提供直接支持,但是因?yàn)镽ESTful是基于HTTP協(xié)議實(shí)現(xiàn)的,所以我們可以利用net/http包來(lái)自己實(shí)現(xiàn),當(dāng)然需要針對(duì)REST做一些改造,REST是根據(jù)不同的method來(lái)處理相應(yīng)的資源,目前已經(jīng)存在的很多自稱是REST的應(yīng)用,其實(shí)并沒(méi)有真正的實(shí)現(xiàn)REST,我暫且把這些應(yīng)用根據(jù)實(shí)現(xiàn)的method分成幾個(gè)級(jí)別,請(qǐng)看下圖:


上圖展示了我們目前實(shí)現(xiàn)REST的三個(gè)level,我們?cè)趹?yīng)用開(kāi)發(fā)的時(shí)候也不一定全部按照RESTful的規(guī)則全部實(shí)現(xiàn)他的方式,因?yàn)橛行r(shí)候完全按照RESTful的方式未必是可行的,RESTful服務(wù)充分利用每一個(gè)HTTP方法,包括DELETE和PUT??捎袝r(shí),HTTP客戶端只能發(fā)出GET和POST請(qǐng)求:

  • HTML標(biāo)準(zhǔn)只能通過(guò)鏈接和表單支持GET和POST。在沒(méi)有Ajax支持的網(wǎng)頁(yè)瀏覽器中不能發(fā)出PUT或DELETE命令
  • 有些防火墻會(huì)擋住HTTP PUT和DELETE請(qǐng)求要繞過(guò)這個(gè)限制,客戶端需要把實(shí)際的PUT和DELETE請(qǐng)求通過(guò) POST 請(qǐng)求穿透過(guò)來(lái)。RESTful 服務(wù)則要負(fù)責(zé)在收到的 POST 請(qǐng)求中找到原始的 HTTP 方法并還原。

我們現(xiàn)在可以通過(guò)POST里面增加隱藏字段_method這種方式可以來(lái)模擬PUT、DELETE等方式,但是服務(wù)器端需要做轉(zhuǎn)換。我現(xiàn)在的項(xiàng)目里面就按照這種方式來(lái)做的REST接口。當(dāng)然Go語(yǔ)言里面完全按照RESTful來(lái)實(shí)現(xiàn)是很容易的,我們通過(guò)下面的例子來(lái)說(shuō)明如何實(shí)現(xiàn)RESTful的應(yīng)用設(shè)計(jì)。

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) {
    uid := r.FormValue("uid")
    fmt.Fprint(w, "you are add user %s", uid)
}

func main() {
    mux := routes.New()
    mux.Get("/user/:uid", getuser)
    mux.Post("/user/", adduser)
    mux.Del("/user/:uid", deleteuser)
    mux.Put("/user/:uid", modifyuser)
    http.Handle("/", mux)
    http.ListenAndServe(":8088", nil)
}

上面的代碼演示了如何編寫一個(gè)REST的應(yīng)用,我們?cè)L問(wèn)的資源是用戶,我們通過(guò)不同的method來(lái)訪問(wèn)不同的函數(shù),這里使用了第三方庫(kù)github.com/drone/routes,在前面章節(jié)我們介紹過(guò)如何實(shí)現(xiàn)自定義的路由器,這個(gè)庫(kù)實(shí)現(xiàn)了自定義路由和方便的路由規(guī)則映射,通過(guò)它,我們可以很方便的實(shí)現(xiàn)REST的架構(gòu)。通過(guò)上面的代碼可知,REST就是根據(jù)不同的method訪問(wèn)同一個(gè)資源的時(shí)候?qū)崿F(xiàn)不同的邏輯處理。

總結(jié)

REST是一種架構(gòu)風(fēng)格,汲取了WWW的成功經(jīng)驗(yàn):無(wú)狀態(tài),以資源為中心,充分利用HTTP協(xié)議和URI協(xié)議,提供統(tǒng)一的接口定義,使得它作為一種設(shè)計(jì)Web服務(wù)的方法而變得流行。在某種意義上,通過(guò)強(qiáng)調(diào)URI和HTTP等早期Internet標(biāo)準(zhǔn),REST是對(duì)大型應(yīng)用程序服務(wù)器時(shí)代之前的Web方式的回歸。目前Go對(duì)于REST的支持還是很簡(jiǎn)單的,通過(guò)實(shí)現(xiàn)自定義的路由規(guī)則,我們就可以為不同的method實(shí)現(xiàn)不同的handle,這樣就實(shí)現(xiàn)了REST的架構(gòu)。

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)