Go & Gin 细碎记录

请求数据无法二次获取

bodyByts, err := ioutil.ReadAll(c.Request.Body)
if err != nil {
  c.String(http.StatusBadRequest, err.Error())
  c.Abort()
}
// 回填请求数据,便于再次获取
c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(bodyByts))

同时处理 GET 和 POST 请求

type Person struct {
  Name string `form:"name"`
  Address string `form:"address"`
  Birthday time.Time `form:"birthday"`
}

func main() {
  r := gin.Default()
  r.GET("/testing", testing)
  r.POST("/testing", testing)
}

func testing(c *gin.Context) {
  var person Person
  // ShouldBind 会根据请求类型(content-type)自动转换参数到指定结构体内
  if err := c.ShouldBind(&person); err == nil {
    // do something
  }
}

参数验证

结构体验证

// https://pkg.go.dev/gopkg.in/go-playground/validator.v8#section-readme
type Person struct {
  Age int `form:"age" binding:"required,gt=10"`
  Name string `form:"name" binding:"required"`
  Address string `from:"address" binding:"required"`
}

自定义验证

validator.v8 是支持自定义验证的。

type Booking struct {
  CheckIn time.Time `form:"check_in" binding:"required,bookabledate" time_format:"2006-01-02"`
  CheckOut time.Time `form:"check_out" binding:"required,gtfield=CheckIn" time_format:"2006-01-02"`
}


// 定义验证方法
func bookableDate(*validator.Validate, reflect.Value, reflect.Value, field reflect.Value, reflect.Type, reflect.Kind, string) bool {
  if date,ok := field.Interface().(time.Time);ok {
    today := time.Now()
    if date.Unix() > today.Unix() {
      return true
    }
  }
  return false
}

func main() {
  r := gin.Default()
  // 注册验证方法
  if v,ok := binding.Validator.Engine().(*validator.Validate);ok {
    v.RegisterValidation("bookabledate", bookableDate)
  }
}

多语言验证

package main

import (
   "github.com/gin-gonic/gin"
   "github.com/go-playground/locales/en"
   "github.com/go-playground/locales/zh"
   "github.com/go-playground/universal-translator"
   "gopkg.in/go-playground/validator.v9"
   en2 "gopkg.in/go-playground/validator.v9/translations/en"
   zh2 "gopkg.in/go-playground/validator.v9/translations/zh"
   "net/http"
   "time"
)

type Booking struct {
   CheckIn time.Time `form:"check_in" validate:"required,bookableDate" time_format:"2006-01-02"`
   CheckOut time.Time `form:"check_out" validate:"required,gtfield=CheckIn" time_format:"2006-01-02"`
}


func main(){

   r := gin.Default()

   zhTranslate := zh.New()
   enTranslate := en.New()
   Uni := ut.New(zhTranslate, enTranslate)

   validate := validator.New()
   _ = validate.RegisterValidation("bookableDate", bookableDate)

   r.GET("/bookable", func(c *gin.Context) {
      locale := c.DefaultQuery("locale", "zh")
      trans, _ := Uni.GetTranslator(locale)
      switch locale {
      case "zh":
         _ = validate.RegisterTranslation("bookableDate", trans, registerBookableDateTranslation, bookableDateTranslation)
         _ = zh2.RegisterDefaultTranslations(validate, trans)
         break
      case "en":
         _ = en2.RegisterDefaultTranslations(validate, trans)
         break
      default:
         _ = validate.RegisterTranslation("bookableDate", trans, registerBookableDateTranslation, bookableDateTranslation)
         _ = zh2.RegisterDefaultTranslations(validate, trans)
      }

      var book Booking
      if err := c.ShouldBind(&book); err != nil {
         c.JSON(http.StatusInternalServerError, gin.H{
            "error": err.Error(),
         })
         c.Abort()
         return
      }
      if err := validate.Struct(book); err != nil {
         errs := err.(validator.ValidationErrors)

         var sliceErrs []string
         for _, v := range errs {
            sliceErrs = append(sliceErrs, v.Translate(trans))
         }

         c.JSON(http.StatusInternalServerError, gin.H{
            "error": sliceErrs,
         })
         c.Abort()
         return
      }

      c.JSON(http.StatusOK, gin.H{
         "message": "OK",
         "booking": book,
      })
   })

   _ = r.Run()
}

func bookableDate(fl validator.FieldLevel) bool {

   if date, ok := fl.Field().Interface().(time.Time); ok {
      today := time.Now()
      if date.Unix() > today.Unix() {
         return true
      }
   }

   return false
}

func registerBookableDateTranslation(ut ut.Translator) error {
   return ut.Add("bookableDate", "{0} 日期必须大于当天日期!", true)
}

func bookableDateTranslation(ut ut.Translator, fe validator.FieldError) string {
   t, _ := ut.T("bookableDate", fe.Field())
   return t
}