### 前言
最近优化[gin+vue的前后端分离项目](https://github.com/Bingjian-Zhu/gin-vue-admin)代码时候,发现代码中对请求数据的校验比较繁琐,于是想办法简化它。最终我发现了[go-playground/validator](https://github.com/go-playground/validator)开源库很好用。
### 优化前代码
代码如下:
![](https://img2020.cnblogs.com/blog/1508611/202004/1508611-20200428135632947-2147280894.png)
发现每个方法都这样校验数据,很繁琐。
### 优化代码
这里使用[go-playground/validator](https://github.com/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`是需要实现的最基本的接口,作为验证引擎来确保请求的正确性。
```go
type StructValidator interface {
ValidateStruct(interface{}) error
Engine() interface{}
}
```
* `ValidateStruct` :如果接收到的类型是一个结构体或指向结构体的指针,则执行验证。
* `Engine`: 返回支持`StructValidator`实现的底层验证引擎。
实现接口:
```go
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`验证
```go
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`函数中添加这行代码:
```go
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的数据请求校验了,接下来看下优化后的代码。
### 优化后代码
![](https://img2020.cnblogs.com/blog/1508611/202004/1508611-20200428144843346-824677511.png)
只需要正常使用`c.Bing(model)`就可以对请求的数据进行校验了,代码简化了许多。
### 常用校验规则介绍
```go
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格式
}
```
更多校验规则可以阅读[源码文档](https://github.com/go-playground/validator/blob/master/doc.go)。
### 总结
[go-playground/validator](https://github.com/go-playground/validator)开源库把gin的请求校验简单化了,使得我们代码更简单易读。
以上只是对结构体做请求校验,对于非结构体的请求校验,用老办法
```go
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)
}
```
源码地址:https://github.com/Bingjian-Zhu/gin-vue-admin
有疑问加站长微信联系(非本文作者))