
本文详解如何使用 Go 的 database/sql 包正确读取 PostgreSQL 返回的 JSON 查询结果,并将其安全、高效地转换为字符串,以便直接响应 HTTP 请求。
本文详解如何使用 go 的 `database/sql` 包正确读取 postgresql 返回的 json 查询结果,并将其安全、高效地转换为字符串,以便直接响应 http 请求。
在 Go 中,sql.Rows 并非一个可直接转为字符串的数据容器,而是一个迭代器接口——它代表查询结果集的游标,必须通过 rows.Next() 遍历,并配合 rows.Scan() 将每行数据提取到 Go 变量中。你代码中的 db.Query("select to_json(t) from (SELECT * from cars) t") 实际返回的是多行单列(JSON 类型)的结果集,而非单一字符串;直接尝试“把 rows 当成字符串”会导致编译错误或运行时 panic。
✅ 正确做法:用 QueryRow 或 Query + Scan 提取 JSON 值
由于 to_json(...) 在 PostgreSQL 中返回的是 json 类型(对应 Go 中的 []byte),推荐使用 sql.NullString 或直接 *string/[]byte 扫描。更稳妥且符合语义的方式是扫描为 []byte,再转 string:
func HelloServer(w http.ResponseWriter, req *http.Request) {
db, err := sql.Open("postgres", "user=postgres password=password dbname=name sslmode=disable")
if err != nil {
http.Error(w, "DB connection failed: "+err.Error(), http.StatusInternalServerError)
return
}
defer db.Close()
// ✅ 推荐:若确定只返回一行一列(如聚合 JSON 数组),用 QueryRow
row := db.QueryRow("SELECT json_agg(t) FROM (SELECT * FROM cars) t")
var jsonBytes []byte
err = row.Scan(&jsonBytes)
if err != nil {
http.Error(w, "Query failed: "+err.Error(), http.StatusInternalServerError)
return
}
// 转为字符串并写入响应体(自动设置 Content-Type 为 text/plain)
w.Header().Set("Content-Type", "application/json; charset=utf-8")
_, _ = w.Write(jsonBytes) // ✅ 安全:jsonBytes 已是 UTF-8 编码的 JSON 字节
}
? 说明:
- json_agg(t) 比 to_json(t) 更适合生成 JSON 数组(对应 []cars);若需单对象数组,可用 ARRAY_TO_JSON(ARRAY_AGG(...))。
- []byte 是 PostgreSQL json/jsonb 列最自然的 Go 对应类型,避免了 string 扫描可能引发的 sql.ErrNoRows 或编码问题。
- 显式设置 Content-Type: application/json 确保浏览器正确解析响应。
⚠️ 注意事项与最佳实践
- 不要忽略错误:rows.Close() 和 rows.Err() 必须检查(尤其在循环后),否则可能掩盖底层 I/O 错误。
- 避免内存泄漏:务必 defer rows.Close()(若用 Query)或确保 row.Scan() 后资源释放(QueryRow 自动管理)。
- SQL 注入防护:示例中连接字符串硬编码仅用于演示;生产环境请使用环境变量或配置中心管理凭证,并对动态查询参数使用参数化语句($1, $2)。
- 性能考虑:若数据量大,建议流式处理或分页,而非一次性加载全部 JSON 到内存。
✅ 总结
Go 中没有 .ToString() 这样的魔法方法——sql.Rows 必须显式迭代和扫描。将 PostgreSQL JSON 结果转为字符串的核心步骤是:
- 用 QueryRow(单行)或 Query + Next()(多行)获取结果;
- Scan 到 []byte 变量(最安全);
- 直接 string(jsonBytes) 或直接 w.Write(jsonBytes) 输出;
- 设置正确的 Content-Type 头以保障前端正确消费。
这样既符合 Go 的显式、安全哲学,也充分发挥了 PostgreSQL 的 JSON 能力。











