Gin请求参数校验

本文详细介绍了如何在Gin框架中使用validator库进行请求参数校验,包括数字、字符串长度、存在性、关联及数组、Map的校验规则。同时,讲解了 Gin 中的数据绑定和校验结果获取方法,提供示例代码帮助理解。
部署运行你感兴趣的模型镜像

gin参数校验使用的是 validator库,因此本文的内容同样适用于使用validator来做参数校验的地方

校验规则

完整的校验规则可参考https://godoc.org/github.com/go-playground/validator,下面分享常用的校验规则规则:

  • 数字值限制

    注:如果限制之间存在冲突,如 eq=10,ne=10,则会根据先后顺序,后面的会覆盖前面的定义,以后面定义的为准;校验内容为eq=10,ne=10,只会生效ne=10,如果存在冲突,所有校验规则均符合此规律。

    • 限制范围
      max=10 # 最大值为10,即小于等于10
      min=10 # 最小值为10,即大于等于10
      gt=10  # 大于 10
      gte=10 # 大于等于10
      lt=10  # 小于10
      lte=10 # 小于等于10
      
    • 限制值
      eq=10 # 等于10
      ne=10 # 不等于10
      oneof=1 2 3 # 值只能是1、2 或 3
      len=3 # 限制位数为3位数
      
  • 字符串长度限制

    • 限制长度范围

      max=10 # 最大长度为10
      min=10 # 最小长度为10
      gt=10  # 长度大于10
      lt=10  # 长度小于10
      gte=10 # 长度大于等于10
      let=10 # 长度小于等于10
      
    • 限制值内容

      eq=aaa # 值为aaa
      ne=aaa # 值不能为aaa
      oneof=a b c # 枚举,只能为a、b 或 c
      len=3 # 字符长度为3
      
  • 存在性校验

    注:这里需要特别注意的是,这里是否存在是根据0值与非0值判断的,如果字段中传了对应的0值,也会被认为是不存在的

    • 必选

      required # 必须
      
    • 可选

      omitempty,xxx=xxx # 可选,如果存在,则继续向后校验规则xxx=xxx,如果不存在,则xxx=xxx不生效,但是如果omitempty之前存在校验规则,则前面的校验规则还是生效的,如 gte=-1,omitempty,len=3,则gte=-1规则始终生效,而len=3只有在值不为0时生效。
      
    • 关联校验

      required_with=AAA # 当AAA存在时,此字段也必须存在
      required_with_all=AAA BBB CCC  # 当AAA BBB CCC 都存在时,此字段必须存在
      required_without=AAA # 当AAA不存在时,此字段必须存在
      required_without_all=AAA BBB # 当AAA BBB 都不存在时,此字段必须存在
      
  • 结构体字段间校验

    • 一个层级内部校验

      eqfield=AAA # 和字段AAA值相等
      nefield=AAA # 和字段AAA值不相等
      gtfield=AAA # 大于字段AAA的值
      gtefield=AAA # 大于等于字段AAA的值
      ltfield=AAA # 小于字段AAA的值
      ltefield=AAA # 小于等于AAA字段的值
      
    • 多个层级之间校验

      eqcsfield=AAA.B  # 等于AAA中的B字段
      necsfield=AAA.B  # 不等于AAA中的B字段
      gtcsfield=AAA.B  # 大于AAA中的B字段
      gtecsfield=AAA.B # 大于等于AAA中的B字段
      ltcsfield=AAA.B  # 小于AAA中的B字段
      ltecsfield=AAA.B # 小于等于AAA中的B字段
      

      注:此处要注意的是,多个结构体之间的校验必须是字段之间存在层级联系,且只能是上层的与下层的做关联,不能反过来,如下:

      type Timeout struct {
      	Connect int `json:"connect" validate:"required"`
      	Read    int `json:"read" validate:"required"`
      	Send    int `json:"send" validate:"required"`
      }
      
      type MyStruct struct {
      	Name    string   `json:"name" validate:"required"`
      	Out     int      `json:"out" validate:"eqcsfield=Timeout.Connect"` // 只能是Timeout同层级的去关联Timeout下的字段,不能Timeout下的字段关联上层字段
      	Timeout Timeout  `json:"timeout"`
      }
      
  • dive 对数组校验

    dive 的意思是潜水,潜水也就是深入到水下,dive在这里的意思是也差不多,就是更深入一层的意思,如当前字段类型是 []string,那么深入一层,就从 整体(array)局部(array中的一个元素),对应到校验上,就是dive前是对整体校验,dive后是对整体中的元素校验。示例:

    • 一般数组

      Domains []string `binding:"gt=0,dive,required,min=1,max=100"`
      

      检验内容:[]string长度必须大于0,数组中元素string长度必须在1-100之间

    • 结构体数组校验

         Cors         []AStruct    `binding:"dive"`
      

      虽然这里dive前后未定义相关校验规则,但是如果要启用 AStruct 中定义的校验规则,那么dive是必须的,否则AStruct中定义的规则不会生效

  • dive对Map校验

    dive的含义在这里仍然类似于数组,整体 -> 局部 的规则不变,但是map的特殊性在于不仅有value值,还有key值,这里就需要方法去区分是校验value,还是校验key;这里使用了 keys 和 endkeys来标记key值的校验范围,从keys开始,至endkeys结束

    ReqHeaders      map[string]string         `binding:"dive,keys,min=1,max=100,endkeys,required,min=1,max=100"`
    

    校验内容:未对整体做校验,限制key值长度必须在1-100之间,value值的长度也必须在1-100之间

  • structonly

    当一个结构体定义了校验规则,但是在某些地方,不需要这些校验规则生效的时候,可以使用structonly标记,存在此标记的结构体内部的校验规则将不会再生效。如下:

    type Timeout struct {
    	Connect int `json:"connect" binding:"required"`
    	Read    int `json:"read" binding:"required"`
    	Send    int `json:"send" binding:"required"`
    }
    
    type MyStruct struct {
    	Name    string   `json:"name" binding:"required"`
    	Timeout Timeout `json:"timeout" binding:"structonly"` 
    }
    

    结构体Timeout各个字段定义了校验内容,但是我在MyStructTimeout字段使用了structonly标记,那么Timeout中定义的校验内容将不再生效

  • nostructlevel

    nostructlevel 类似于structonly,唯一不同之处是要想忽略规则的效果生效,必须要使用requiredomitempty

    type Timeout struct {
    	Connect int `json:"connect" binding:"required,gte=1,lte=1000"`
    	Read    int `json:"read" binding:"gte=1,lte=1000"`
    	Send    int `json:"send" binding:"gte=1,lte=1000"`
    }
    
    type MyStruct struct {
    	Name    string   `form:"name" json:"name" binding:"required"`
    	UseMeta bool     `json:"use_meta"`
    	Timeout Timeout `json:"timeout" binding:"required,nostructlevel"`
    }
    
  • 忽略校验规则

    - # 忽略字段校验规则,常用于嵌套结构中,用来忽略嵌套结构下的校验规则
    
  • 多校验规则运算

    | # 多个检验规则默认之间是与关系,使用 | 可实现多运算规则之间进行或运算
    

gin中的使用方法

1. 校验参数定义

校验参数是定义在结构体上的,因此在首先需要定义好接受数据的结构体,再定义每个属性的校验参数:

type Timeout struct {
	Connect int `binding:"omitempty,gte=1,lte=75"`
	Read    int `binding:"omitempty,gte=1,lte=1000"`
	Send    int `binding:"omitempty,gte=1,lte=1000"`
}

type MyTestStruct struct {
	Domains []string `binding:"gt=0,dive,required,min=1,max=100"`
	Name    string   `binding:"required"`
	UseTimeout bool
	Timeout Timeout `binding:"required_with=UseTimeout"`
}

结构体字段 binding tag的内容就是校验参数。

2. 数据绑定

gin中提供了常用的 ShouldBindWith/ShouldBindUri方法,来实现自动的数据与结构体的绑定,提供了以下类型的数据:

数据类型TagContext-Type数据源使用示例
JSONjsonapplication/jsonBodyShouldBindWith(&data, binding.Query)
XMLxmlapplication/xml、text/xmlBodyShouldBindWith(&data, binding.XML)
Formformapplication/x-www-form-urlencodedBodyc.ShouldBindWith(&data, binding.Form)
Queryformurl queryShouldBindWith(&data, binding.Query)
FormPostformapplication/x-www-form-urlencodedBodyShouldBindWith(&data, binding.FormPost)
FormMultipartformmultipart/form-dataBodyShouldBindWith(&data, binding.FormMultipart)
ProtoBufapplication/x-protobufBodyShouldBindWith(&data, binding.ProtoBuf)
MsgPackapplication/x-msgpack、application/msgpackBodyShouldBindWith(&data, binding.MsgPack)
YAMLapplication/x-yamlBodyShouldBindWith(&data, binding.YAML)
UriuriuriShouldBindUri(&data)
HeaderheaderHeaderShouldBindWith(&forwardRule, binding.Header)

说明:

  • 数据类型:指的是传输的数据类型

  • Tag:结构体定义的Tag,揭示了数据字段与结构体字段的对应关系,如:Form类型数据中 id对应结构体中的ID字段,那么就需要定义一下内容:

    type xxxx struct {
    	...
    	ID int64 `form:"id"`
    	...
    }
    
  • Context-Type:HTTP中的Header 字段之一,表示Body的数据类型,gin.Context中的Bind方法会根据Content-Type自动绑定数据到对应类型上

    func (c *Context) Bind(obj interface{}) error {
    	b := binding.Default(c.Request.Method, c.ContentType()) // 获取数据类型
    	return c.MustBindWith(obj, b) // 校验数据
    }
    

    注:MustBindWithShouldBindWith的区别是MustBindWith会自动返回错误信息,如果你有统一的错误返回格式,MustBindWith可能会破坏返回格式的统一性。

  • 数据源:指的是从哪里取数据,Body指的是从HTTP Body中获取数据,url query指从URL的quey参数中获取数据,url指的是从url中获取参数,如:/api/v1/xxx/:id,id参数就是从url中来获取的

  • 使用示例:使用的例程

3. 校验结果获取

ShouldBindWith会返回error信息,如果error信息不为空,则表明出现错误,错误信息就包含在error中,如果error为空,则表明校验通过,示例如下:

var data MyTestStruct // 存储数据内容
// 获取 & 校验数据
if err = c.ShouldBindWith(&data, binding.JSON); err != nil {
  // 出现错误,打印错误内容
  fmt.Println(err)
  return
}
// 校验成功,打印数据内容
fmt.Println(data)

您可能感兴趣的与本文相关的镜像

Yolo-v8.3

Yolo-v8.3

Yolo

YOLO(You Only Look Once)是一种流行的物体检测和图像分割模型,由华盛顿大学的Joseph Redmon 和Ali Farhadi 开发。 YOLO 于2015 年推出,因其高速和高精度而广受欢迎

<think>首先,用户的问题是关于在Gin框架中如何读取请求体中的JSON数据。具体来说,是“Gin框架如何从请求体中解析JSON数据”。 根据系统级指令,我需要: - 使用中文回答。 - 所有行内数学表达式必须用$...$格式(但这里可能不涉及数学表达式)。 - 独立公式用$$...$$格式(同样,可能不适用)。 - LaTeX语法正确。 - 生成相关问题。 - 在回答中引用的段落末尾自然地添加引用标识,如[^1]。 - 回答结构清晰,帮助用户逐步解决问题。 - 参考提供的引用内容。 提供的引用: - [^1]: 在Gin框架中优雅地处理HTTP请求体中的JSON数据。介绍了Gin框架基础。 - [^2]: gin源码阅读(2)请求体中的JSON参数是如何解析的?提到了BindJSON, Bind, ShouldBindJSON, ShouldBind函数。 - [^3]: Go: 探索 Gin 框架的 HTTP 请求体解析。讨论了定义结构体来接收数据。 用户的问题直接引用这些引用中的内容,所以我应该基于这些来构建回答。 回答结构: 1. **介绍Gin框架**:简要说明Gin是什么。 2. **解析JSON数据的步骤**:逐步解释如何在Gin中读取和解析JSON请求体。 3. **使用相关函数**:如BindJSON, ShouldBindJSON等。 4. **示例代码**:提供代码示例来演示。 5. **注意事项**:提到错误处理或最佳实践。 6. **生成相关问题**:在最后添加相关问题。 关键点从引用中提取: - 从[^2],Gin提供了函数如BindJSON和ShouldBindJSON来解析JSON。 - 从[^3],需要定义结构体来映射请求体。 - 从[^1],Gin是高性能的Go框架。 回答应该真实可靠,所以我基于标准Gin实践。 逐步解决问题: 1. **导入Gin包**。 2. **定义路由和处理函数**。 3. **在函数中,使用BindJSON或类似函数来解析JSON到结构体**。 4. **处理错误**。 5. **响应**。 代码示例应该使用Go语言。 最后,添加相关问题,如示例中的格式。 完整回答结构: - 开头解释。 - 步骤说明。 - 代码块。 - 相关问题。</think>### Gin框架中如何读取和解析请求体中的JSON数据 在Gin框架中,从HTTP请求体中读取和解析JSON数据是一个常见且高效的任务。Gin是一个高性能的Go语言Web框架,提供了内置函数来简化JSON数据的处理过程。核心思想是:定义一个Go结构体(struct)来映射JSON数据的字段,然后使用Gin提供的绑定函数将请求体中的JSON解析到该结构体中。以下我将逐步解释这个过程,确保回答清晰可靠。 #### 步骤1: 定义结构体映射JSON数据 首先,你需要定义一个Go结构体,其字段对应JSON请求体的键(key)。结构体字段应使用JSON标签(tag)来指定JSON键的名称。这确保了Gin能正确地将JSON数据映射到结构体字段上[^3]。 例如,假设你的JSON请求体格式如下: ```json { "name": "Alice", "age": 30, "email": "alice@example.com" } ``` 对应的结构体定义应为: ```go type User struct { Name string `json:"name"` // JSON标签指定键名 Age int `json:"age"` Email string `json:"email"` } ``` #### 步骤2: 使用Gin的绑定函数解析JSON Gin提供了多种函数来解析JSON请求体。最常用的是`ShouldBindJSON`和`BindJSON`,它们都是基于`context.Context`对象的方法。区别在于错误处理方式: - **ShouldBindJSON**: 如果解析失败(如JSON格式错误或字段不匹配),它返回错误但不中止请求,适合自定义错误处理。 - **BindJSON**: 解析失败时会自动返回400错误响应,适合快速失败场景。 在路由处理函数中,调用这些函数将请求体JSON绑定到你定义的结构体[^2]。 以下是一个完整的示例代码,展示如何在Gin中实现: ```go package main import ( "net/http" "github.com/gin-gonic/gin" ) // 步骤1: 定义结构体 type User struct { Name string `json:"name"` Age int `json:"age"` Email string `json:"email"` } func main() { router := gin.Default() // 创建Gin引擎实例 // 定义POST路由,处理JSON请求 router.POST("/user", func(c *gin.Context) { var user User // 创建结构体实例 // 步骤2: 使用ShouldBindJSON解析JSON请求体 if err := c.ShouldBindJSON(&user); err != nil { // 错误处理:返回400状态码和错误信息 c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } // 解析成功,处理数据(例如打印或存储) c.JSON(http.StatusOK, gin.H{ "message": "User data received", "user": user, // 返回解析后的结构体 }) }) router.Run(":8080") // 启动服务器在8080端口 } ``` #### 关键注意事项 - **错误处理**: 总是检查绑定函数的返回值(如`err != nil`),以处理无效JSON或缺失字段。这能防止应用崩溃[^2]。 - **性能优化**: Gin的绑定函数内部使用高效的JSON解析库(如Go的`encoding/json`),但对于高并发场景,考虑使用`ShouldBindBodyWith`来复用请求体数据[^2]。 - **安全性**: 确保结构体字段类型匹配JSON数据类型(如字符串、整数),避免类型转换错误。必要时,使用验证库(如`go-playground/validator`)添加额外校验[^1]。 - **其他绑定函数**: 除了JSON,Gin还支持XML、表单等数据格式,使用类似函数如`ShouldBind`或`Bind`[^2]。 通过以上步骤,你可以在Gin中优雅地读取和解析JSON请求体。这种方法确保了代码可维护性和可靠性,符合Gin框架的设计哲学[^1][^3]。
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值