以 Go 語言快速實作 HTTP GET/POST API

(同步發表於 Cepave)

維基百科中提到:

目前在三種主流的Web服務實現方案中,因為REST模式與複雜的SOAP和XML-RPC相比更加簡潔,越來越多的web服務開始採用REST風格設計和實現。

符合 REST 設計風格的 Web API 稱為 RESTful API。它從以下三個方面資源進行定義:直觀簡短的資源地址、傳輸的資源、對資源的操作。

那麼我們要如何使用 Go 語言原生的函式庫建立簡單的 Web Server 提供 GET/POST 接口呢?讓我們透過 Hello World 的範例來說明。

建立 HTTP Server 接收請求

  1. 透過
    http.HandleFunc("/", callback)
    ,我們可以定義 callback 函式所要負責的 URI。
  2. 呼叫
    http.Request.ParseForm()
    取出參數。同時適用於 GET/POST,所以為了減少行數我將範例中 GET/POST 一併處理。
  3. 透過
    http.Request.FormValue("key")
    取出姓氏與名字。
  4. 最後,將字串以
    http.ResponseWriter.Write("Hello World")
    輸出,client 端即可在網頁上看到對應的訊息囉!
// server.go package main

import (
    "fmt"
    "net/http"
)

func main() {
    http.HandleFunc("/cepave", func(w http.ResponseWriter, req *http.Request) {
        req.ParseForm()
        if req.Method == "GET" || req.Method == "POST" {
            fmt.Println(req.ContentLength)
            firstname := req.FormValue("firstname")
            lastname := req.FormValue("lastname")
            w.Write([]byte(fmt.Sprintf("[%s] Hello, %s %s!", req.Method, firstname, lastname)))
        } else {
            http.Error(w, "The method is not allowed.", http.StatusMethodNotAllowed)
        }
    })

err := http.ListenAndServe(":9000", nil)
    if err != nil {
        fmt.Println("ListenAndServe failed: ", err)
    }
}

實際發送請求來試試!

完成了 Server 端程式之後,我們可以透過

go run server.go
開始運行。 HTTP GET 可以直接以瀏覽器輸入 URL 即可測試,但既然都寫了 server.go 不如也把 client.go 實現吧!

GET/POST 發送請由分別是透過

http.Get(...)
以及
http.PostForm(...)
來達成。在 client.go 裡面之所以多 import 兩個函式庫的原因是為了要儲存 HTTP POST 的參數,並且處理送出請求後返回的 HTTP response。
// client.go package main

import (
    "fmt"
    "io/ioutil"
    "net/http"
    "net/url"
)

func main() {
    // POST
    resp, err := http.PostForm("http://localhost:9000/cepave",
        url.Values{"firstname": {"Kordan"}, "lastname": {"Ou"}})
    if err != nil {
        fmt.Println(err)
    } else {
        body, _ := ioutil.ReadAll(resp.Body)
        fmt.Println("POST OK: ", string(body), resp)
    }

// GET
    resp, err = http.Get("http://localhost:9000/cepave?firstname=Kordan&lastname=Ou")
    if err != nil {
        fmt.Println(err)
    } else {
        body, _ := ioutil.ReadAll(resp.Body)
        fmt.Println("GET OK: ", string(body), resp)
    }

}

如果只是想測試 API,我們也可以打開終端機直接輸入以下命令:

$ curl "localhost:9000/cepave?firstname=Kordan&lastname=Ou" [GET] Hello, Kordan Ou!

$curl -X POST -d "firstname=Kordan&&lastname=Ou" localhost:9000/cepave
[POST] Hello, Kordan Ou!

如果是有巢狀結構的 JSON,我們可以這樣發 HTTP POST:

package main

import (
    "bytes"
    "fmt"
    "io/ioutil"
    "net/http"
)

func main() {
    var jsonStr = []byte({     "endpoint_counters": [         {             "endpoint": "host1",             "counter": "load.1min"         },         {             "endpoint": "host2",             "counter": "cpu.idle"         }     ],     "start": 1437375109,     "end": 1437377109,     "cf": "AVERAGE"     })

url := "http://example.com/graph/history"
    fmt.Println("URL:>", url)

req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr))
    req.Header.Set("X-Custom-Header", "myvalue")
    req.Header.Set("Content-Type", "application/json")
    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()

fmt.Println("response Status:", resp.Status)
    fmt.Println("response Headers:", resp.Header)
    body, _ := ioutil.ReadAll(resp.Body)
    fmt.Println("response Body:", string(body))

}

透過以上幾個範例,應該可以感受到 Go 語言的簡潔以及內建函式庫的強大吧?!

其他參考資料

  1. http://www.restapitutorial.com/lessons/httpmethods.html
  2. http://www.evanlin.com/golangheroku-rest-api-web-application/
  3. http://dougblack.io/words/a-restful-micro-framework-in-go.html