使用MongoDB与Golang时遇到error类型无解码器错误
在MongoDB中存储Golang error类型的解决方案
Golang的error是接口类型,MongoDB的bson驱动默认仅能处理基础类型和明确的结构体类型。存入时error会被隐式转为底层实现(比如*errors.errorString)的字段,但查询解码时,驱动无法将存储的映射数据还原为error接口实例,因此抛出no decoder found for error错误。
下面是几种可行的解决方法:
方案1:将error转为字符串存储(最简单)
直接把结构体中的Error字段改为string类型,存储时调用error.Error()存入字符串,查询后如果需要error实例,再通过errors.New()或fmt.Errorf()转换:
import "errors" type Step struct { Name string CompensateFunc interface{} ErrorStr string `bson:"error,omitempty"` // 替换原Error字段 Result interface{} } // 存入时处理 step := Step{ Name: "test", ErrorStr: err.Error(), // err是你的error实例 } // 查询后还原error var step Step // ... 执行查询逻辑 ... var err error if step.ErrorStr != "" { err = errors.New(step.ErrorStr) }
方案2:自定义可编解码的Error类型
如果需要保留error接口的特性,可以自定义一个实现error接口的类型,同时实现bson.Marshaler和bson.Unmarshaler接口,手动控制编解码逻辑:
import ( "errors" "go.mongodb.org/mongo-driver/bson" ) type StorableError struct { Msg string `bson:"msg"` } // 实现error接口 func (e *StorableError) Error() string { return e.Msg } // 实现bson.Marshaler接口 func (e *StorableError) MarshalBSON() ([]byte, error) { if e == nil { return bson.Marshal(nil) } return bson.Marshal(bson.M{"msg": e.Msg}) } // 实现bson.Unmarshaler接口 func (e *StorableError) UnmarshalBSON(data []byte) error { var m bson.M if err := bson.Unmarshal(data, &m); err != nil { return err } msg, ok := m["msg"].(string) if !ok { return errors.New("invalid error format in MongoDB") } e.Msg = msg return nil } // 修改Step结构体 type Step struct { Name string CompensateFunc interface{} Error *StorableError `bson:"error,omitempty"` Result interface{} } // 使用示例 // 存入时:如果原err是普通error,转换为StorableError step.Error = &StorableError{Msg: err.Error()} // 查询后直接使用step.Error,它本身就是error接口实例
方案3:利用bson的Hook(进阶)
如果你不想修改原结构体,可以注册一个全局的bson编解码Hook,自动处理error类型的编解码:
import ( "errors" "reflect" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/bsoncodec" "go.mongodb.org/mongo-driver/bson/bsonrw" ) // 自定义error编解码器 type errorCodec struct{} func (ec *errorCodec) EncodeValue(ecctx bsoncodec.EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error { if val.Kind() == reflect.Ptr && val.IsNil() { return vw.WriteNull() } errVal := val.Interface().(error) return vw.WriteString(errVal.Error()) } func (ec *errorCodec) DecodeValue(dcctx bsoncodec.DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error { str, err := vr.ReadString() if err != nil { if err == bsonrw.ErrNull { val.Set(reflect.Zero(val.Type())) return nil } return err } errObj := errors.New(str) val.Set(reflect.ValueOf(errObj)) return nil } // 注册Hook到bson编码器 func registerErrorCodec() { registry := bson.NewRegistry() registry.RegisterTypeEncoder(reflect.TypeOf((*error)(nil)).Elem(), &errorCodec{}) registry.RegisterTypeDecoder(reflect.TypeOf((*error)(nil)).Elem(), &errorCodec{}) // 创建MongoDB客户端时使用这个registry // clientOpts := options.Client().ApplyURI(uri).SetRegistry(registry) }
内容的提问来源于stack exchange,提问作者N P
相关产品推荐
相关产品推荐

