如何为SQLC生成的Params结构体添加Gin验证且无需重构?
为SQLC生成的Params结构体添加验证的几种实用方案
问题背景
用SQLC根据PostgreSQL Schema生成结构体、配合Gin开发Web API时,发现SQLC的override配置只能给主表结构体(比如User)加validate标签,没法作用于查询生成的Params结构体(比如CreateUserBasicParams)。想直接把前端JSON绑定到Params结构体做验证,又不想重复定义几乎一模一样的新结构体。
方案1:手写自定义验证逻辑(最简单直接)
不用改结构体,在Gin的handler里直接对Params字段做检查:
func CreateUserHandler(c *gin.Context) { var params hmdb.CreateUserBasicParams if err := c.ShouldBindJSON(¶ms); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } // 逐一验证必填字段 if params.Username == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "用户名不能为空"}) return } if params.Email == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "邮箱不能为空"}) return } if !params.Phone.Valid { c.JSON(http.StatusBadRequest, gin.H{"error": "手机号不能为空"}) return } // 调用SQLC生成的创建用户方法 user, err := db.CreateUserBasic(c, params) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, user) }
方案2:用结构体嵌入扩展验证标签
创建一个带验证标签的结构体,嵌入SQLC生成的Params结构体,既复用原结构又能加验证:
import "github.com/go-playground/validator/v10" // 嵌入原Params结构体,重写字段添加验证标签 type CreateUserBasicParamsWithValidation struct { hmdb.CreateUserBasicParams Username string `json:"username" validate:"required"` Email string `json:"email" validate:"required,email"` Phone pgtype.Text `json:"phone" validate:"required"` } func CreateUserHandler(c *gin.Context) { var params CreateUserBasicParamsWithValidation if err := c.ShouldBindJSON(¶ms); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } // 用validator做统一验证 validate := validator.New() if err := validate.Struct(¶ms); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } // 转成SQLC要求的Params结构体 sqlcParams := hmdb.CreateUserBasicParams(params.CreateUserBasicParams) user, err := db.CreateUserBasic(c, sqlcParams) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, user) }
方案3:自定义SQLC模板(批量解决)
SQLC支持自定义生成模板,可以修改Params结构体的生成逻辑,自动给字段加验证标签:
- 从SQLC源码仓库复制默认的
params.tmpl模板文件到本地项目 - 修改模板内容,在字段的标签部分添加
validate:"required"(可以根据字段是否允许为空做逻辑判断) - 在
sqlc.yaml里指定自定义模板路径:
version: 2 sql: - engine: "postgresql" schema: ["./schema"] queries: ["./queries"] strict_function_checks: true gen: go: emit_json_tags: true sql_package: "pgx/v5" package: "hmdb" out: "hmdb" template: params: "./templates/params.tmpl" # 你的自定义模板路径
方案4:反射动态修改标签(不推荐生产用)
通过反射在运行时给结构体字段加标签,但这种方式绕过Go类型安全,只适合临时测试场景:
import ( "fmt" "reflect" "unsafe" ) func addValidateTag(obj interface{}, fieldName, tagContent string) error { val := reflect.ValueOf(obj).Elem() field, ok := val.Type().FieldByName(fieldName) if !ok { return fmt.Errorf("找不到字段:%s", fieldName) } // 拼接原有标签和新的validate标签 jsonTag := field.Tag.Get("json") newTag := fmt.Sprintf(`json:"%s" validate:"%s"`, jsonTag, tagContent) // 用unsafe修改结构体标签(生产环境慎用) tagPtr := (*reflect.StringHeader)(unsafe.Pointer(&field.Tag)) newTagHeader := reflect.StringHeader{Data: unsafe.StringData(newTag), Len: len(newTag)} *tagPtr = newTagHeader return nil } // 使用示例 // var params hmdb.CreateUserBasicParams // addValidateTag(¶ms, "Username", "required")
内容的提问来源于stack exchange,提问作者Goodies
相关产品推荐
相关产品推荐

