如何结合Go Validator与sqlc 无需定义两个结构体?
//
// 此结构体由sqlC自动生成
//
type CreateTable1Params struct {
Columnname1 string json:"columnname1"
Columnname2 string json:"columnname2"
}
func (q *Queries) CreateTable1(ctx context.Context, arg CreateTable1Params ) (Table1, error) {
...
}
现在我需要验证CreateTable1的REST参数(比如columnName2为必填项),因此手动编写了控制器代码`table1Controller.go`: ```go import ( ... validator "github.com/go-playground/validator/v10" ) type Table1Controller struct { Validate *validator.Validate } // // 手动创建用于验证的结构体 // type InT1Param struct { ColumnName1 string `validate:"required" form:"columnName1" json:"columnName1" db:"columnname1"` ColumnName2 string `validate:"required" form:"columnName2" json:"columnName2" db:"columnname2" binding="required"` } func (c *Table1Controller) validateInput(t1 InT1Param) error { err := service.Validate.Struct(t1) if err != nil { errStr := "" for _, mapErr := range err.(validator.ValidationErrors) { errStr = fmt.Sprintf("%s%s\n", errStr, mapErr.Translate(service.Translator)) } return errors.New(errStr) } return nil } func (c *Table1Controller) Insert(ctx *gin.Context, dbQueries *dbModel.Queries, t1 InT1Param) error { err := c.validateInput(t1) if err != nil { return err } inParam := dbModel.CreateTable1Params { Columnname1: t1.ColumnName1, Columnname2: t1.ColumnName2 } _, err := dbQueries.CreateTable1(ctx, inParam) if err != nil { return err } return nil }
我想知道是否可以不用手动创建InT1Param结构体,直接让sqlc生成的结构体支持validator验证?
当然可以,无需维护重复的结构体,以下是几种实用方案:
1. 自定义sqlc模板自动添加验证标签
这是最彻底的方案,通过修改sqlc的结构体生成模板,让它根据表的约束(如NOT NULL)自动为字段添加validate标签。
操作步骤:
- 复制sqlc默认的Go结构体模板(基于官方基础模板修改即可)
- 修改模板逻辑:在生成字段tag的部分,对
NOT NULL的字段追加validate:"required"标签 - 在
sqlc.yaml配置文件中指定使用该自定义模板
示例模板片段:
{{ range .Columns }} {{ .Name | ToCamel }} {{ .Type }} `json:"{{ .Name | ToSnake }}" db:"{{ .Name | ToSnake }}"{{ if .NotNull }} validate:"required"{{ end }}` {{ end }}
修改后,sqlc生成的CreateTable1Params会自动带上验证标签,直接用它调用validator.Struct()即可完成验证。
2. 运行时为生成的结构体注册验证规则
如果不想修改模板,可以在程序启动阶段,给validator注册针对CreateTable1Params的自定义验证逻辑:
func init() { validate := validator.New() // 为CreateTable1Params注册必填验证 _ = validate.RegisterStructValidation(func(sl validator.StructLevel) { params := sl.Current().Interface().(dbModel.CreateTable1Params) if params.Columnname1 == "" { sl.ReportError(params.Columnname1, "Columnname1", "columnname1", "required", "") } if params.Columnname2 == "" { sl.ReportError(params.Columnname2, "Columnname2", "columnname2", "required", "") } }, dbModel.CreateTable1Params{}) }
之后直接使用validate.Struct(createParams)验证sqlc生成的结构体即可。
3. 嵌入结构体复用字段(适用于需要额外标签的场景)
如果需要给字段添加form这类额外标签,可将sqlc生成的结构体嵌入到自定义结构体中,避免重复定义字段:
type InT1Param struct { dbModel.CreateTable1Params // 可在此添加额外字段或覆盖特定字段的标签(若需要) } // 若要调整标签,也可直接重写字段但复用类型: type InT1Param struct { Columnname1 string `validate:"required" form:"columnName1" json:"columnname1" db:"columnname1"` Columnname2 string `validate:"required" form:"columnName2" json:"columnname2" db:"columnname2"` } // 使用时直接类型转换: createParams := dbModel.CreateTable1Params{...} inParam := InT1Param(createParams)
其中自定义模板的方案最省心,一次配置后所有生成的结构体都会自动带上符合数据库约束的验证规则,无需后续手动维护。
内容的提问来源于stack exchange,提问作者saavedrah

