Go1.18下Gin校验JSON数组对象报错及自定义校验需求求助
解决Gin框架JSON参数校验的两个问题:
required,dive报错与questions_id存在性校验 问题背景
使用Go 1.18结合Gin框架校验JSON请求参数时,遇到两个核心问题:
- 为
AnswerList字段添加required,dive标签后,触发reflect: call of reflect.Value.Interface on zero Value错误 - 需要实现
answer_list数组中每个元素的questions_ids必须对应真实存在的题目ID,否则校验失败
请求的JSON结构:
{ "questions_batch_id": "1", "answer_list": [ { "questions_ids": "1", "answer": [ "2222","3333" ] } ] }
初始校验结构体:
type Answer struct { QuestionsID string `json:"questions_id" binding:"required"` Answer []string `json:"answer" binding:"required"` } type AnswerQuestionParams struct { QuestionBatchId string `json:"questions_batch_id" binding:"required"` AnswerList []*Answer `json:"answer_list" binding:"required"` UserId string `json:"user_id" binding:"-"` UserType string `json:"user_type" binding:"-"` }
错误原因分析
- 字段名不匹配:JSON中的键是
questions_ids,但结构体中QuestionsID的json tag是questions_id(少了末尾的s),导致Gin无法正确解析该字段,required校验规则失效。 dive标签误用时机:当answer_list未传值或为空数组时,dive标签会尝试对零值切片的元素进行校验,触发反射操作错误。
修复方案
步骤1:修正字段Tag与校验标签
先修正结构体的字段映射关系,再正确使用required,dive标签:
type Answer struct { // 修正json tag为questions_ids,匹配JSON请求中的键名 QuestionsID string `json:"questions_ids" binding:"required"` Answer []string `json:"answer" binding:"required"` } type AnswerQuestionParams struct { QuestionBatchId string `json:"questions_batch_id" binding:"required"` // 添加dive标签,确保切片非空且每个元素执行自身校验规则 AnswerList []*Answer `json:"answer_list" binding:"required,dive"` UserId string `json:"user_id" binding:"-"` UserType string `json:"user_type" binding:"-"` }
解释:required,dive组合会先校验切片本身非空,再遍历切片中的每个Answer对象,执行其字段的校验规则。
步骤2:实现questions_id存在性的自定义校验
Gin默认校验器不支持业务级的存在性校验,需要自定义校验函数:
- 注册自定义校验器:
import ( "github.com/go-playground/validator/v10" "github.com/gin-gonic/gin" ) // 自定义校验函数:检查questions_id是否存在于业务数据源(如数据库) func validateQuestionId(fl validator.FieldLevel) bool { questionId := fl.Field().String() // 替换为你的实际业务逻辑,比如查询数据库判断ID有效性 return checkQuestionExists(questionId) } // 程序初始化时注册自定义校验规则 func init() { if v, ok := gin.DefaultValidator.(*validator.Validate); ok { // 注册名为question_exist的校验标签 _ = v.RegisterValidation("question_exist", validateQuestionId) } } // 模拟业务校验函数,实际替换为数据库查询 func checkQuestionExists(questionId string) bool { // 示例:校验ID是否在有效集合中 validQuestionIds := map[string]bool{"1": true, "2": true, "3": true} return validQuestionIds[questionId] }
- 在结构体中使用自定义标签:
type Answer struct { // 添加question_exist标签,触发自定义校验 QuestionsID string `json:"questions_ids" binding:"required,question_exist"` Answer []string `json:"answer" binding:"required"` }
这样,当answer_list中的questions_ids不存在时,Gin会返回校验失败的错误。
额外优化建议
如果需要确保answer数组非空,可以给Answer结构体的Answer字段添加min=1标签:
Answer []string `json:"answer" binding:"required,min=1"`
内容的提问来源于stack exchange,提问作者DomainRocker
相关产品推荐
相关产品推荐

