Table of Contents
请求数据无法二次获取
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 }