在 Go Echo 框架中操作 HTTP 請求 (1)

在上一篇文章中,我們討論瞭如何在 Echo 框架中設置嵌套樣板。

在接下來的部分,我們將繼續介紹如何在 Echo 服務器中發出 HTTP 請求並將結果呈現在客戶端。讓我們從上一篇文章中的例子開始。

設置 API 端點

首先,創建一個新的 API 端點(endpoint)來返回一個簡單的 JSON ,這個稍後會被 Echo 服務器本身調用。

在 api 文件夾中建立新端點

與寫死的 JSON 請求不同,這個新 API 端點的請求結構將被定義在 model/example_request.go 這文件中,在實現時它的內容可以是一個 JSON 物件、HTML 表格或網址參數所提供,然後將其返回給客戶端。

model/example_request.go

1
2
3
4
5
6
package model

type ExampleRequest struct {
  FirstName string `json:"first_name" form:"first_name" query:"first_name"`
  LastName  string `json:"last_name" form:"last_name" query:"last_name"`
}

api/get_full_name.go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package api

import (
  "fmt"
  "net/http"

  "github.com/labstack/echo"

  "gitlab.com/ykyuen/golang-echo-template-example/model"
)

func GetFullName(c echo.Context) error {
  // Bind the input data to ExampleRequest
  exampleRequest := new(model.ExampleRequest)
  if err := c.Bind(exampleRequest); err != nil {
    return err
  }

  // Manipulate the input data
  greeting := exampleRequest.FirstName + " " + exampleRequest.LastName

  return c.JSONBlob(
    http.StatusOK,
    []byte(
      fmt.Sprintf(`{
        "first_name": %q,
        "last_name": %q,
        "msg": "Hello %s"
      }`, exampleRequest.FirstName, exampleRequest.LastName, greeting),
    ),
  )
}

設置上述 API 端點的路由

main.go 中添加我們的新 API 端點路由。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
...
func main() {
  // Echo instance
  e := echo.New()

  // Instantiate a template registry with an array of template set
  // Ref: https://gist.github.com/rand99/808e6e9702c00ce64803d94abff65678
  templates := make(map[string]*template.Template)
  templates["home.html"] = template.Must(template.ParseFiles("view/home.html", "view/base.html"))
  templates["about.html"] = template.Must(template.ParseFiles("view/about.html", "view/base.html"))
  e.Renderer = &TemplateRegistry{
    templates: templates,
  }

  // Route => handler
  e.GET("/", handler.HomeHandler)
  e.GET("/about", handler.AboutHandler)

  // Route => api
  e.GET("/api/get-full-name", api.GetFullName)

  // Start the Echo server
  e.Logger.Fatal(e.Start(":1323"))
}

測試新的 API 端點

運行 Echo 服務器來測試一下。

成功!

成功!

這個新端點可以直接為瀏覽器或其它客戶端提供服務,也可以被 Echo 服務器本身調用。

從 Echo 服務器調用 API

建立回應結構型別

model/example_request.go 類似,我們需要定義一個 HTTP 回應結構型別。

model/example_response.go

1
2
3
4
5
6
7
package model

type ExampleResponse struct {
  FirstName string `json:"first_name"`
  LastName  string `json:"last_name"`
  Msg       string `json:"msg"`
}

在 about 頁面中顯示回應的資訊

修改 about handler 的代碼,使其顯示 HTTP 回應的資訊。

handler/about_handler.go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package handler

import (
  "encoding/json"
  "io/ioutil"
  "net/http"

  "github.com/labstack/echo"

  "gitlab.com/ykyuen/golang-echo-template-example/model"
)

func AboutHandler(c echo.Context) error {

  tr := &http.Transport{}
  client := &http.Client{Transport: tr}

  // Call the api
  resp, err := client.Get(
    "http://localhost:1323/api/get-full-name?first_name=Kit&last_name=Yuen",
  )

  if err != nil {
    return err
  }

  defer resp.Body.Close()
  body, err := ioutil.ReadAll(resp.Body)

  // Unmarshal the response into a ExampleResponse struct
  var exampleResponse model.ExampleResponse
  if err = json.Unmarshal(body, &exampleResponse); err != nil {
    return err
  }

  // Please note the the second parameter "about.html" is the template name and should
  // be equal to one of the keys in the TemplateRegistry array defined in main.go
  return c.Render(http.StatusOK, "about.html", map[string]interface{}{
    "name": "About",
    "msg": exampleResponse.Msg,
  })
}

測試 about 頁面

啟動 Echo 服務器並瀏覽 about 頁面,我們可以看到新的問候語。

這個問候語現在由調用 API 後所得到的回應組成。

這個問候語現在由調用 API 後所得到的回應組成。

項目結構

進行上述更改後,項目結構應如下圖所示。(項目重命名為 handling-http-request-in-go-echo-example-1,如果要更改項目名稱,請確保正確設置 Golang 代碼中的 import 語句。)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
handling-http-request-in-go-echo-example-1/
  ├── api/                     # folder of api endpoints
  │   ├── get_full_name.go     # api for get full name
  ├── handler/                 # folder of request handlers
  │   ├── home_handler.go      # handler for home page
  │   └── about_handler.go     # handler for about page
  ├── model/                   # folder of custom struct types
  │   ├── example_request.go   # struct type of get_full_name request
  │   └── example_response.go  # hstruct type of get_full_name response
  ├── vendor/                  # dependencies managed by dep
  │   ├── github.com/*
  │   └── golang.org/*
  ├── view/                    # folder of html templates
  │   ├── base.html            # base layout template
  │   ├── home.html            # home page template
  │   └── about.html           # about page template
  ├── Gopkg.lock               # dep config file
  ├── Gopkg.toml               # dep config file
  └── main.go                  # programme entrypoint

總結

本文介紹如何在 Echo 框架中設置簡單的 API 端點。由於 Go 是一種強型別語言,因此我們必須先將 JSON 請求與回應的結構型別,然後才能操作其內容。例如:

  • api/get_full_name.go 將 GET 請求綁定到 exampleRequest
  • handler/about_handler.go 將 GET 回應解組為 exampleResponse

要調用 API,我們可以直接使用 Go 內建的 net/http 標準函式庫。

完整的例子可以在此 gitlab.com 代碼庫上找到。