
本文介绍一种简洁、类型安全的 go 语言通用 http get + json 解析封装方案,通过接收指针参数避免反射或类型断言,消除重复逻辑,同时保持编译时类型检查。
在构建 API 客户端时,大量重复的 HTTP GET → 响应校验 → 读取 Body → JSON 反序列化流程极易导致代码冗余和维护困难。核心痛点在于:只有目标结构体类型不同,其余逻辑完全一致。此时,一个类型安全、无需 interface{} 类型断言、也不依赖 reflect 的泛型式封装是最优解。
Go 1.18+ 推荐使用泛型(更现代、更安全),但即使在旧版本中,也可通过传入指向目标变量的指针实现零成本抽象。关键在于:json.Unmarshal 本身要求第二个参数是 interface{},且该接口值必须是一个可寻址的指针(如 &users),而非指针的地址(如 &target)。常见错误正是误将 interface{} 变量取地址,导致反序列化写入的是 target 的副本,而非原始变量。
✅ 正确做法如下(兼容 Go 1.17+):
import (
"encoding/json"
"io"
"net/http"
"errors"
)
// request 执行 GET 请求,将响应 JSON 解析到 target 指向的变量中
// target 必须为指向切片或结构体的指针,例如 &[]User{} 或 &Project{}
func request(url string, target interface{}) error {
resp, err := http.Get(url)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return errors.New("non-200 status code: " + resp.Status)
}
body, err := io.ReadAll(resp.Body)
if err != nil {
return err
}
return json.Unmarshal(body, target) // ✅ 直接传 target(它已是 *[]User 等)
}使用方式清晰直观:
func getUsers(url string) ([]User, error) {
var users []User
if err := request(url, &users); err != nil {
return nil, err
}
return users, nil
}
func getProject(url string) (Project, error) {
var proj Project
if err := request(url, &proj); err != nil {
return Project{}, err
}
return proj, nil
}⚠️ 注意事项:
- target 参数必须传指针(&users),否则 json.Unmarshal 会报错 json: Unmarshal(nil *[]User);
- 不要尝试在函数内对 target 再取地址(如 &target),这会导致解包到临时接口变量,原变量不变;
- 错误处理建议按需增强:可返回 *http.Response 供调用方检查 Header/Status,或封装自定义错误类型(如含 StatusCode 字段);
- 生产环境推荐使用带超时的 http.Client 替代 http.Get,例如:
client := &http.Client{Timeout: 10 * time.Second} resp, err := client.Get(url)
? 进阶提示(Go 1.18+):若项目已升级,可进一步改用泛型函数,获得更强的类型推导与 IDE 支持:
func Get[T any](url string) (T, error) {
var result T
if err := request(url, &result); err != nil {
return result, err
}
return result, nil
}
// 使用:users, err := Get[[]User](url)该模式彻底解耦网络层与业务数据结构,让每个 API 调用仅关注「URL 是什么」和「我要什么类型」,显著提升可读性、可测性与可维护性。










