上周因同事休假暂时帮忙维护一个服务,周五下午本以为要顺利度过本周的时候线上出了问题,在进行数据修复的时候发现之前一个工具代码的隐藏 bug,该 bug 会在进行数据修复时触发 panic。剥离具体业务逻辑之后代码逻辑如下:
1 | func main() { |
main
函数逻辑很简单:从数据库中查询某个数据,将该数据转换为map[string]interface{}
,再通过 JsonBuilder
根据JsonPath
对数据进行修改,其中依赖的其他函数如下:
1 | func StructToMap(v interface{}) (map[string]interface{}, error) { |
在讨论为何会引发 panic 之前,有几个背景知识需要交代清楚:
json.Marshal(v any) ([]byte, error)
函数如果入参为 nil 则返回的 bytes 内容为null
json.Unmarshal(data []byte, v any)
函数如果入参data
为null
则函数执行结束之后 V 的值为 nil- golang 的 interface 的内部实现两个字段:
type
和data
,只有两个字段都为 nil 时interface == nil
才为true
这里引发 panic 的主要原因是StructToMap
返回的 map 为 nil,在后续将其赋值给了builder.data
, 而 data
为interface
类型,因此该interface
的type
为map[string]interface{}
,而该interface
的data
为 nil
,最终导致if b.data == nil
的判断语句失效,进而没有进行数据初始化引发了 panic。
这里只需将if b.data == nil
修改为if b.data == nil || reflect.ValueOf(b.data).IsNil()
即可准确的判断出 interface
是否为 nil,从而完成数据初始化避免 panic
golang 中的 inteface 是否为 nil 的判断已经是老生常谈的问题了,但在实际开发过程中还是经常被忽略掉,再搭配到 json 包一起使用,一不小心还是很容易踩坑。