Redigo从Redis读取Sorted Set结果时默认返回[]byte切片,若直接JSON序列化会导致二进制数据被错误编码为Base64字符串;正确做法是显式转换为字符串切片,避免Go JSON包对[]byte的自动base64编码行为。
redigo从redis读取sorted set结果时默认返回`[]byte`切片,若直接json序列化会导致二进制数据被错误编码为base64字符串;正确做法是显式转换为字符串切片,避免go json包对`[]byte`的自动base64编码行为。
在使用 Redigo 操作 Redis Sorted Set 并结合 JSON 序列化时,一个常见却容易被忽视的问题是:从 ZREVRANGE 等命令获取的响应值,在 Go 中实际是 []interface{},其中每个元素为 []byte 类型(即原始字节切片),而非 string。而 Go 的 json.Marshal 在遇到 []byte 时,会自动将其编码为 Base64 字符串(RFC 4648),这正是你看到类似 "eyJmcm9tSWQiOjEsInRvSWQiOjUs... 这种“奇怪结果”的根本原因。
例如,你存入 Redis 的是:
b, _ := json.Marshal(&Notification{...}) // b 是 []byte,如 { "fromId":5, ... }
c.Send("ZADD", key, score, string(b)) // 注意:这里已转为 string 存储
但 Redigo 的 c.Receive() 返回的是 []interface{},其元素类型仍是 []byte(即使 Redis 中存的是字符串——Redigo 客户端底层统一按字节流处理响应),因此:
❌ 错误用法(导致 Base64):
reply, _ := c.Receive() json.Marshal(reply) // reply 中每个 []byte 被 encode 成 base64
✅ 正确做法:将 []interface{} 中每个 []byte 显式转为 string。
Redigo 提供了便捷的类型转换辅助函数 redis.Strings(),它专为此类场景设计:接收 redis.Reply(即 c.Do(...) 返回值),并安全地将 []interface{} 中每个 []byte 元素转为 string,返回 []string:
func ByUserId(userId int64, pool *redis.Pool) ([]string, error) {
key := fmt.Sprintf("user:%d:notification", userId)
c := pool.Get()
defer c.Close()
// 使用 Do() + redis.Strings() 一行完成类型转换
return redis.Strings(c.Do("ZREVRANGE", key, 0, -1))
}
该方案相比手动遍历 []interface{} 更简洁、健壮,且内置了类型检查与错误处理(如某元素非 []byte 会返回 redis.ErrNil 或类型错误)。
? 补充说明与最佳实践:
- 不要在 ZADD 时传 string(b) 再次编码:你的 Create() 方法中 c.Send("ZADD", key, n.CreatedTime, string(b)) 是正确的——因为 Redis 命令期望字符串值,string(b) 是必要转换。
- 但读取时绝不应再次 json.Marshal([]byte):[]byte 是二进制载体,不是待序列化的 Go 值;它本就是合法 JSON 字符串的字节表示。
-
服务端响应建议直接返回结构体切片(更安全):若前端需要标准 JSON 数组,可进一步解析字符串为结构体,避免传输冗余字符串:
func ByUserIdAsStructs(userId int64, pool *redis.Pool) ([]Notification, error) { strs, err := ByUserId(userId, pool) if err != nil { return nil, err } var notifications []Notification for _, s := range strs { var n Notification if err := json.Unmarshal([]byte(s), &n); err != nil { return nil, fmt.Errorf("invalid notification JSON: %w", err) } notifications = append(notifications, n) } return notifications, nil } - 注意 Redis 数据一致性:确保写入时 json.Marshal 无误,且 FromUsername/FromImage 等字段经校验后未引入非法字符(影响 JSON 解析)。
总结:Redigo 的类型抽象虽便利,但也要求开发者明确响应数据的实际类型。牢记 []byte → string 的显式转换是跨过 JSON Base64 陷阱的关键一步,而 redis.Strings() 是最符合 Go 习惯、最简洁可靠的解决方案。











