Go 每日一庫之 gorilla-schema

簡介

gorilla/schema 是 gorilla 開發工具包中用於處理表單的庫。它提供了一個簡單的方式,可以很方便地將表單數據轉爲結構體對象,或者將結構體對象轉爲表單數據。

快速使用

本文代碼使用 Go Modules。

創建目錄並初始化:

$ mkdir gorilla/schema && cd gorilla/schema
$ go mod init github.com/darjun/go-daily-lib/gorilla/schema

安裝gorilla/schema庫:

$ go get -u github.com/gorilla/schema

我們還是拿前面登錄的例子:

func index(w http.ResponseWriter, r *http.Request) {
  fmt.Fprintln(w, "Hello World")
}

func login(w http.ResponseWriter, r *http.Request) {
  fmt.Fprintln(w, `<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta >
  <title>Login</title>
</head>
<body>
<form action="/login" method="post">
  <label>Username:</label>
  <input ><br>
  <label>Password:</label>
  <input ><br>
  <button type="submit">登錄</button>
</form>
</body>
</html>`)
}

type User struct {
  Username string `schema:"username"`
  Password string `schema:"password"`
}

var (
  decoder = schema.NewDecoder()
)

func dologin(w http.ResponseWriter, r *http.Request) {
  r.ParseForm()
  u := User{}
  decoder.Decode(&u, r.PostForm)
  if u.Username == "dj" && u.Password == "handsome" {
    http.Redirect(w, r, "/", 301)
    return
  }

  http.Redirect(w, r, "/login", 301)
}

func main() {
  r := mux.NewRouter()
  r.HandleFunc("/", index)
  r.Handle("/login", handlers.MethodHandler{
    "GET":  http.HandlerFunc(login),
    "POST": http.HandlerFunc(dologin),
  })
  log.Fatal(http.ListenAndServe(":8080", r))
}

首先調用schema.NewDecoder()方法創建一個解碼器decoder。在處理器中,先調用r.ParseForm()解析表單數據,然後創建用戶對象u,調用decoder.Decode(&u, r.PostForm)通過表單數據填充該對象。

schema使用反射來對應表單和結構體字段,我們可以通過結構體標籤來指定表單數據和字段的對應關係。

上面我們將解碼器作爲一個包內的全局變量,因爲decoder中會緩存一些結構體的元數據,並且它是併發安全的

main函數中,我們創建了gorilla/mux路由,註冊/根處理函數,使用中間件handlers.MethodHandler分別註冊路徑/login的 GET 和 POST 方法的處理器。然後調用http.Handle("/", r)將所有請求交由gorilla/mux路由處理。最後啓動 Web 服務器接受請求。

編碼

除了用於服務器解碼錶單數據外,schema還可用於客戶端,將結構體對象編碼到表單數據中發送給服務器。我們編寫一個程序登錄上面的服務器:

var (
  encoder = schema.NewEncoder()
)

func main() {
  client := &http.Client{}
  form := url.Values{}

  u := &User{
    Username: "dj",
    Password: "handsome",
  }
  encoder.Encode(u, form)

  res, _ := client.PostForm("http://localhost:8080/login", form)
  data, _ := ioutil.ReadAll(res.Body)
  fmt.Println(string(data))
  res.Body.Close()
}

與解碼器的用法類似,首先調用schema.NewEncoder()創建一個編碼器encoder,創建一個User類型的對象u和表單數據對象form,調用encoder.Encode(u, form)u編碼到form中。然後使用http.ClientPostForm方法發送請求。讀取響應。

自定義類型轉換

目前schema支持以下類型:

有時候客戶端會將一個切片拼成一個字符串傳到服務器,服務器收到之後需要解析成切片:

type Person struct {
  Name    string   `schema:"name"`
  Age     int      `schema:"age"`
  Hobbies []string `schema:"hobbies"`
}

var (
  decoder = schema.NewDecoder()
)

func init() {
  decoder.RegisterConverter([]string{}, func(s string) reflect.Value {
    return reflect.ValueOf(strings.Split(s, ","))
  })
}

func doinfo(w http.ResponseWriter, r *http.Request) {
  r.ParseForm()
  p := Person{}
  decoder.Decode(&p, r.PostForm)

  fmt.Println(p)
  fmt.Fprintf(w, "Name:%s Age:%d Hobbies:%v", p.Name, p.Age, p.Hobbies)
}

調用decoder.RegisterConverter()註冊對應類型的轉換函數,轉換函數類型爲:

func(s string) reflect.Value

即將請求中的字符串值轉爲滿足我們格式的值。

客戶端請求:

type Person struct {
  Name    string `schema:"name"`
  Age     int    `schema:"age"`
  Hobbies string `schema:"hobbies"`
}

var (
  encoder = schema.NewEncoder()
)

func main() {
  client := &http.Client{}
  form := url.Values{}

  p := &Person{
    Name:    "dj",
    Age:     18,
    Hobbies: "Game,Programming",
  }
  encoder.Encode(p, form)

  res, _ := client.PostForm("http://localhost:8080/info", form)
  data, _ := ioutil.ReadAll(res.Body)
  fmt.Println(string(data))
  res.Body.Close()
}

客戶端故意將Hobbies字段設置爲字符串,發送請求。服務器將使用註冊的func (s string) reflect.Value函數將該字符串切割爲[]string返回。

總結

schema提供了一個簡單的獲取表單數據的方式,通過將數據填充到結構體對象中,我們可以很方便的進行後續操作。schema庫比較小巧,對特性沒太多要求的可以試試~

大家如果發現好玩、好用的 Go 語言庫,歡迎到 Go 每日一庫 GitHub 上提交 issue😄

參考

  1. gorilla/schema GitHub:github.com/gorilla/schema

  2. Go 每日一庫 GitHub:https://github.com/darjun/go-daily-lib

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/88WNqhxq6RacbK2Ev9JDTg