Validator自定义校验规则
Go语言中广泛使用的validator
库支持通过结构体标签定义校验规则。当内置规则无法满足需求时,我们可以轻松扩展自定义校验逻辑。
示例场景:验证用户年龄是否成年(≥18岁)
javascript
type User struct {
Age int`validate:"adult"`
}
func TestValidator(t *testing.T) {
// 注册自定义校验规则
validate := validator.New()
_ = validate.RegisterValidation("adult", func(fl validator.FieldLevel) bool {
return fl.Field().Int() >= 18
})
// 验证通过案例
user := User{Age: 10}
err := validate.Struct(user)
if err != nil {
fmt.Println(err)
}
}
运行后会输出:
javascript
Key: 'User.Age' Error:Field validation for 'Age' failed on the 'adult' tag
实现要点:
1.使用RegisterValidation
注册自定义校验标签
2.通过Field().Int()
获取字段值
3.返回布尔值表示校验结果
sync.Pool优化对象复用
对于频繁创建/销毁的对象,使用sync.Pool
可以显著降低GC压力,提升性能。
JSON编码优化示例:
javascript
var bufferPool = sync.Pool{
New: func() interface{} { return new(bytes.Buffer) },
}
func EncodeJSON(v interface{}) ([]byte, error) {
buf := bufferPool.Get().(*bytes.Buffer)
defer bufferPool.Put(buf)
buf.Reset() // 关键:清空旧数据
err := json.NewEncoder(buf).Encode(v)
return buf.Bytes(), err
}
使用注意:
- 取出对象后必须调用Reset()
- 对象生命周期不受池管理
- 适合重量级对象的复用
sync.WaitGroup的正确使用
错误地在goroutine内部调用Add()会导致竞态条件:
javascript
// 危险用法
for i := 0; i < 10; i++ {
go func() {
wg.Add(1) // 并发写入导致竞态
defer wg.Done()
}()
}
正确做法:
javascript
// 安全用法
for i := 0; i < 10; i++ {
wg.Add(1) // 在goroutine外部提前Add
go func() {
defer wg.Done()
}()
}
wg.Wait()
关键原则:
1.Add()调用必须在goroutine外部
2.Done()与Add()次数严格匹配
3.Wait()要在所有Add()完成后调用
nil的语义陷阱
不同类型的nil表现
javascript
var (
p *int // nil pointer
s []int // nil slice
m map[int]int // nil map
f func() // nil function
i interface{} // nil interface
)
危险的nil比较
javascript
var p *int = nil
fmt.Println(p == nil) // true
fmt.Println(i == p) // false!
原理剖析:
- 接口变量包含
(type, value)
二元组 - 当存储nil指针时,类型信息仍然存在
- 与纯nil接口比较时会返回false
安全实践:
1.使用明确的类型断言检查
2.避免直接比较接口与具体类型指针
3.使用反射进行深度nil检查