Gin 模型绑定验证

同其它的框架有点不一样,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 协议》,转载必须注明作者和本文链接
pardon110
讨论数量: 3

测试用标准库自带的 net/http/httptest 不更顺手嘛 :smirk:

2周前 评论

验证包现在好像 validator.v10

2周前 评论

请勿发布不友善或者负能量的内容。与人为善,比聪明更重要!