如何在两个Go应用间共享存在细微差异的结构体?
解决Go项目中结构体ID类型复用的两种方案
针对你遇到的API用primitive.ObjectId、前端想用string的结构体复用问题,这里有两个实用方案:
方案一:自定义兼容型ID类型(推荐)
在第三方模块里定义一个同时支持primitive.ObjectId和string的自定义类型,实现bson和json的序列化/反序列化接口,这样API和前端可以直接复用同一个结构体。
代码示例:
package models import ( "encoding/json" "fmt" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" ) // 自定义ID类型,自动适配MongoDB的ObjectId和JSON字符串 type ObjectIDString string // 实现bson.Getter,让MongoDB将其存储为ObjectId func (o ObjectIDString) GetBSON() (interface{}, error) { oid, err := primitive.ObjectIDFromHex(string(o)) if err != nil { return nil, fmt.Errorf("无效的ObjectId格式: %w", err) } return oid, nil } // 实现bson.Setter,从MongoDB读取时转为字符串 func (o *ObjectIDString) SetBSON(raw bson.Raw) error { var oid primitive.ObjectId if err := raw.Unmarshal(&oid); err != nil { return err } *o = ObjectIDString(oid.Hex()) return nil } // 实现json.MarshalJSON,序列化时输出字符串 func (o ObjectIDString) MarshalJSON() ([]byte, error) { return json.Marshal(string(o)) } // 实现json.UnmarshalJSON,反序列化时解析字符串 func (o *ObjectIDString) UnmarshalJSON(data []byte) error { var s string if err := json.Unmarshal(data, &s); err != nil { return err } // 可选:验证字符串是否为合法ObjectId if _, err := primitive.ObjectIDFromHex(s); err != nil { return fmt.Errorf("无效的ObjectId格式: %w", err) } *o = ObjectIDString(s) return nil } // 通用User结构体,API和前端直接复用 type User struct { Id ObjectIDString `bson:"_id,omitempty" json:"id"` Username string `bson:"username" json:"username"` Tag int `bson:"tag" json:"tag"` }
这样API端和MongoDB交互时,bson会自动把ObjectIDString转成primitive.ObjectId存储;前端从API获取JSON时,json解析会直接得到字符串类型的Id,完全不需要额外处理。
方案二:基础结构体嵌入(轻量方案)
如果觉得自定义类型的序列化代码太繁琐,可以把公共字段抽成基础结构体,API和前端各自嵌入并定义自己的Id字段,减少重复代码。
第三方模块代码:
package models // 公共基础结构体,存放API和前端共用的字段 type BaseUser struct { Username string Tag int }
API端代码:
import ( "go.mongodb.org/mongo-driver/bson/primitive" "your-module-path/models" ) // API专用User,嵌入基础结构体并定义ObjectId类型的Id type User struct { models.BaseUser Id primitive.ObjectId `bson:"_id,omitempty"` }
前端代码:
import "your-module-path/models" // 前端专用User,嵌入基础结构体并定义string类型的Id type User struct { models.BaseUser Id string `json:"id"` }
这个方案的优点是代码量少,不需要处理序列化逻辑;缺点是API和前端还是要各自定义User结构体,但公共字段已经复用,不用重复编写。
内容的提问来源于stack exchange,提问作者raphael.oester
相关产品推荐
相关产品推荐

