如何在应用层校验Go结构体/bson.M与MongoDB JSON Schema?
Go应用层校验MongoDB JSON Schema方案
针对你提到的两种数据类型(Go结构体Person和bson.M),可以通过以下库和方法在应用层完成MongoDB JSON Schema的校验,实现提前失败并支持测试:
1. 使用gojsonschema库(推荐)
这是Go生态中成熟的JSON Schema校验库,支持JSON Schema Draft 04/06/07规范,能直接对接MongoDB的JSON Schema定义。
校验bson.M类型
将bson.M序列化为MongoDB兼容的扩展JSON,再传入库中校验:
import ( "fmt" "github.com/xeipuuv/gojsonschema" "go.mongodb.org/mongo-driver/bson" ) // 定义MongoDB的JSON Schema var mongoSchema = gojsonschema.NewStringLoader(`{ "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "properties": { "name": {"type": "string"}, "age": {"type": "integer", "minimum": 0} }, "required": ["name"] }`) func ValidateBsonM(data bson.M) error { // 将bson.M转为MongoDB扩展JSON jsonBytes, err := bson.MarshalExtJSON(data, false, false) if err != nil { return fmt.Errorf("序列化bson.M失败: %w", err) } documentLoader := gojsonschema.NewBytesLoader(jsonBytes) result, err := gojsonschema.Validate(mongoSchema, documentLoader) if err != nil { return fmt.Errorf("Schema校验出错: %w", err) } if !result.Valid() { var errMsgs []string for _, err := range result.Errors() { errMsgs = append(errMsgs, err.String()) } return fmt.Errorf("数据不符合Schema规则: %v", errMsgs) } return nil }
校验Person结构体
先将结构体实例序列化为JSON(确保字段标签与Schema对应),再执行校验:
import "encoding/json" type Person struct { Name string `bson:"name" json:"name"` // 同时指定bson和json标签,保证序列化一致性 Age int `bson:"age" json:"age"` } func ValidatePerson(p Person) error { jsonBytes, err := json.Marshal(p) if err != nil { return fmt.Errorf("序列化Person失败: %w", err) } documentLoader := gojsonschema.NewBytesLoader(jsonBytes) result, err := gojsonschema.Validate(mongoSchema, documentLoader) if err != nil { return fmt.Errorf("Schema校验出错: %w", err) } if !result.Valid() { var errMsgs []string for _, err := range result.Errors() { errMsgs = append(errMsgs, err.String()) } return fmt.Errorf("数据不符合Schema规则: %v", errMsgs) } return nil }
2. 自定义校验逻辑(轻量场景)
如果不想依赖第三方库,可以手动解析MongoDB的JSON Schema,针对两种数据类型编写校验逻辑:
- 对于
Person结构体:利用反射遍历字段,对照Schema的类型、必填项、范围等规则逐一校验。 - 对于
bson.M:直接遍历键值对,匹配Schema定义的规则进行校验。
这种方式适合Schema规则简单的场景,灵活性高但需要自己实现完整的校验逻辑。
3. 应用层测试支持
不管采用哪种方案,都可以编写单元测试用例验证校验逻辑:
import "testing" func TestValidatePerson(t *testing.T) { // 测试合法数据 validPerson := Person{Name: "Bob", Age: 28} if err := ValidatePerson(validPerson); err != nil { t.Fatalf("合法Person校验失败: %v", err) } // 测试非法数据(缺少必填字段name) invalidPerson := Person{Age: 17} if err := ValidatePerson(invalidPerson); err == nil { t.Fatal("缺少name的Person应该校验失败") } } func TestValidateBsonM(t *testing.T) { // 测试合法数据 validData := bson.M{"name": "Charlie", "age": 35} if err := ValidateBsonM(validData); err != nil { t.Fatalf("合法bson.M校验失败: %v", err) } // 测试非法数据(age为负数) invalidData := bson.M{"name": "Dave", "age": -5} if err := ValidateBsonM(invalidData); err == nil { t.Fatal("age为负数的bson.M应该校验失败") } }
内容的提问来源于stack exchange,提问作者Alexander Mills
相关产品推荐
相关产品推荐

