前言
最近優化gin+vue的前后端分離項目代碼時候,發現代碼中對請求數據的校驗比較繁瑣,於是想辦法簡化它。最終我發現了go-playground/validator開源庫很好用。
優化前代碼
代碼如下:
發現每個方法都這樣校驗數據,很繁瑣。
優化代碼
這里使用go-playground/validator開源庫來簡化請求校驗。
1.安裝go-playground/validator
# 使用 Go Modules
go env -w GO111MODULE=on
# 安裝 go-playground/validator
go get github.com/go-playground/validator/v10
注意:v10版本是使用Go Modules,運行
go get github.com/go-playground/validator/v10
前需要確保GO111MODULE=on
,不然會報:cannot find package "github.com/go-playground/validator/v10"
2.實現StructValidator
接口的兩個方法
StructValidator
是需要實現的最基本的接口,作為驗證引擎來確保請求的正確性。
type StructValidator interface {
ValidateStruct(interface{}) error
Engine() interface{}
}
ValidateStruct
:如果接收到的類型是一個結構體或指向結構體的指針,則執行驗證。Engine
: 返回支持StructValidator
實現的底層驗證引擎。
實現接口:
package validator
import (
"reflect"
"sync"
"github.com/gin-gonic/gin/binding"
"github.com/go-playground/validator/v10"
)
type DefaultValidator struct {
once sync.Once
validate *validator.Validate
}
var _ binding.StructValidator = &DefaultValidator{}
// ValidateStruct 如果接收到的類型是一個結構體或指向結構體的指針,則執行驗證。
func (v *DefaultValidator) ValidateStruct(obj interface{}) error {
if kindOfData(obj) == reflect.Struct {
v.lazyinit()
//如果傳遞不合規則的值,則返回InvalidValidationError,否則返回nil。
///如果返回err != nil,可通過err.(validator.ValidationErrors)來訪問錯誤數組。
if err := v.validate.Struct(obj); err != nil {
return err
}
}
return nil
}
// Engine 返回支持`StructValidator`實現的底層驗證引擎
func (v *DefaultValidator) Engine() interface{} {
v.lazyinit()
return v.validate
}
func (v *DefaultValidator) lazyinit() {
v.once.Do(func() {
v.validate = validator.New()
v.validate.SetTagName("validate")
// //v8版本,v8版本使用"binding"
// v.validate.SetTagName("binding")
})
}
func kindOfData(data interface{}) reflect.Kind {
value := reflect.ValueOf(data)
valueType := value.Kind()
if valueType == reflect.Ptr {
valueType = value.Elem().Kind()
}
return valueType
}
3.使用該驗證引擎
修改model
,添加validate
驗證
type Article struct {
ID int `gorm:"primary_key" json:"id"`
State int `json:"state" validate:"min=0,max=1"`
TagID int `json:"tag_id" validate:"gt=0"`
Title string `json:"title" validate:"required"`
Desc string `json:"desc" validate:"required"`
Content string `json:"content" validate:"required"`
CoverImageURL string `json:"cover_image_url"`
CreatedBy string `json:"created_by" validate:"required"`
ModifiedBy string `json:"modified_by"`
}
最后,只需在main
函數中添加這行代碼:
package main
import (
"github.com/gin-gonic/gin/binding"
"github.com/bingjian-zhu/gin-vue-admin/common/validator"
)
func main() {
binding.Validator = new(validator.DefaultValidator)
// regular gin logic
}
以上,我們就完成了gin的數據請求校驗了,接下來看下優化后的代碼。
優化后代碼
只需要正常使用c.Bing(model)
就可以對請求的數據進行校驗了,代碼簡化了許多。
常用校驗規則介紹
type Test struct {
ID int `validate:"required"` //數字確保不為0
Name string `validate:"required,min=1,max=8"` //字符串確保不為"",且長度 >=1 && <=8 (min=1,max=8等於gt=0,lt=9)
Value string `validate:"required,gte=1,lte=8"` //字符串確保不為"",且長度 >=1 && <=8
Status int `validate:"min=1,max=10"` //最小為0,最大為10(min=0,max=10等於gt=0,lt=11)
PhoneNumber string `validate:"required,len=11"` //不為""且長度為11
Time string `validate:"datetime=2006-01-02"` //必須如2006-01-02的datetime格式
Color string `validate:"oneof=red green"` //是能是red或者green
Size int `validate:"oneof=37 39 41"` //是能是37或者39或者41
Email string `validate:"email"` //必須郵件格式
JSON string `validate:"json"` //必須json格式
URL string `validate:"url"` //必須url格式
UUID string `validate:"uuid"` //必須uuid格式
}
更多校驗規則可以閱讀源碼文檔。
總結
go-playground/validator開源庫把gin的請求校驗簡單化了,使得我們代碼更簡單易讀。
以上只是對結構體做請求校驗,對於非結構體的請求校驗,用老辦法
import "github.com/astaxie/beego/validation"
func (a *Article) GetArticle(c *gin.Context) {
id, _ := strconv.Atoi(c.Param("id"))
valid := validation.Validation{}
valid.Min(id, 1, "id").Message("ID必須大於0")
var data *models.Article
code := codes.InvalidParams
if !valid.HasErrors() {
data = a.Service.GetArticle(id)
code = codes.SUCCESS
} else {
for _, err := range valid.Errors {
a.Log.Info("err.key: %s, err.message: %s", err.Key, err.Message)
}
}
RespData(c, http.StatusOK, code, data)
}