Gin 模型繫結驗證

pardon110發表於2019-11-10

同其它的框架有點不一樣,Go中的web框架模型繫結,更多的是基於結構體標籤。但總體流程還是一致,即後端對來自請求的各種資料型別進行驗證,然後丟擲相應的錯誤資訊。Gin框架預設使用的是結構體驗證器,其核心介面StructValidator 只需關注兩個,即資料驗證和驗證錯誤訊息的返回,簡單明瞭。

繫結

gin/binding 內建模型繫結實現,將請求資料提取到合適的繫結器

Gin 主要提供了兩組繫結方法Must bindShould bind
前者使用框架自帶的處理機制,基本上驗證不透過,就會被終止或丟擲特定的錯誤頁面。
後者存在繫結錯誤,這個錯誤會被返回, 需要開發者去處理相應的請求和錯誤,這樣具有更大的靈活性。

Gin 框架本身已經實現了多種繫結,通常用來繫結來自請求資料,有不同的結構體例項與之對應。其實現的繫結有 JSON, XML, Form,Query,FormPost,FormMultipart,ProtoBuf,MsgPack,YAML,Uri,顯然 Query是對查詢字串的繫結。

驗證

validator.v8 驗證器包,主要是解決驗證規則的解析。實現了基於結構體值驗證,及基於標籤的單欄位斷言。針對內嵌結構體,也提供了跨欄位和跨結構體驗證,可處理任何型別的對映和陣列。通常的做法是,驗證後,提供本地的錯誤型別例項。

示例中的 gtfield通用欄位驗證標籤,而自定義的驗證規則bookabledate,是對日期過濾。基本規則是確保出時間在進時間之後,且這兩個時間都在系統當前時間(即未來發生)之後,否則報錯。

流程

  1. 定義資料模型結構體
  2. 確定驗證規則,包括跨欄位驗證
  3. 將請求資料模型使用合理的繫結器
  4. 處理繫結訊息結果,決定錯誤訊息是否渲染頁面及檢視

程式碼

下述程式碼對同一資料模型使用了多組標籤,如 form,time_format,binding,這樣可以在同一物件上實現不同的效果展示 。binding 標籤是在繫結過程中進行的驗證,form標籤從請求的欄位資料,time_format標籤的時間格式模板

package main

import (
    "net/http"
    "reflect"
    "time"

    "github.com/gin-gonic/gin"
    "github.com/gin-gonic/gin/binding"
    "gopkg.in/go-playground/validator.v8"
)

// Booking 包含繫結和驗證規則
type Booking struct {
    CheckIn  time.Time `form:"check_in" binding:"required,bookabledate" time_format:"2019-01-02"`
    CheckOut time.Time `form:"check_out" binding:"required,gtfield=CheckIn" time_format:"2019-01-02"`
}
// 自定義驗證規則斷言
func bookableDate(
    v *validator.Validate, topStruct reflect.Value, currentStructOrField reflect.Value,
    field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string,
) bool {
    if date, ok := field.Interface().(time.Time); ok {
        today := time.Now()
        if today.After(date) {
            return false
        }
    }
    return true
}

func main() {
    route := gin.Default()
    // 註冊驗證
    if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
        v.RegisterValidation("bookabledate", bookableDate)
    }

    route.GET("/bookable", getBookable)
    route.Run(":8085")
}

func getBookable(c *gin.Context) {
    var b Booking
    // 資料模型繫結查詢字串驗證
    if err := c.ShouldBindWith(&b, binding.Query); err == nil {
        c.JSON(http.StatusOK, gin.H{"message": "Booking dates are valid!"})
    } else {
        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
    }
}

測試

假定當前時間是 2019-11-10,根據自定義的驗證規則 bookabledate, 那麼進出時間必須在該時間之後,否則不透過,效果如下

$ curl "localhost:8085/bookable?check_in=2019-11-11&check_out=2019-11-17"
{"message":"Booking dates are valid!"}

$ curl "localhost:8085/bookable?check_in=2019-03-08&check_out=2019-03-09"
{"error":"Key: 'Booking.CheckIn' Error:Field validation for 'CheckIn' failed on the 'bookabledate' tag"}

補充

  • 多使用工具,window用vscode,linux環境下用vim-go 使用程式碼補全,溯源定義,利於快速的定位使用。像結構體標籤vim-go 可用命令:GoAddTags 快速讓生成,會節省不少時間
  • 測試簡單的建議用iehttp, nc 等命令列工具,簡單不用寫表單,表頭前者天生支援json。至於Curl 工具雖然很強大,但對人並不是很友好。
  • 專門的api介面測試,可用 postman 結合指令碼自動化測試,並持久化測試請求,不必每次重新來過。
本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章