
本文介绍在 Go 泛型普及前(如 Go本文介绍在 go 泛型普及前(如 go interface{} 和指针传递安全、简洁地实现通用 json 请求函数,避免类型断言和冗余代码。
要让
Request.Run()方法支持任意结构体类型的响应解析(如AppStatusInfo、UserProducts等),关键在于解耦类型约束,而非硬编码具体指针类型。Go 的encoding/json包原生支持interface{}参数——只要传入的是指向目标结构体的指针,json.Unmarshal或其封装函数就能正确反序列化。因此,只需将方法签名从:
func (request *Request) Run(responseObject *AppStatusInfo) *errors.Error改为:
func (request *Request) Run(responseObject interface{}) *errors.Error并确保调用时传入对应结构体变量的地址(
&asi、&up),即可实现零侵入式泛化。注意:DecodeJsonRequestBody内部必须直接将responseObject传给json.Unmarshal(resp.Body, responseObject)(不加&),否则会因重复取址导致 panic(例如传入&asi后再&responseObject得到**AppStatusInfo,非法)。以下是重构后的完整
Run方法示例(含关键修复):func (request *Request) Run(responseObject interface{}) *errors.Error { req, err := http.NewRequest(http.MethodGet, request.Url, nil) if err != nil { return &errors.UnknownError } req.Header.Set("Content-Type", "application/json") client := &http.Client{ Timeout: 5 * time.Second, } resp, err := client.Do(req) if err != nil { return &errors.UnknownError } defer resp.Body.Close() // ✅ 必须在 error 检查后立即 defer,防止 body 泄漏 if err := DecodeJsonRequestBody(resp, responseObject); err != nil { // 直接传入 responseObject return &errors.UnknownError } return nil }使用方式保持自然且类型安全:
var appStatus AppStatusInfo if err := req.Run(&appStatus); err != nil { log.Fatal(err) } // appStatus 已被正确填充 var products UserProducts if err := req.Run(&products); err != nil { log.Fatal(err) } // products 已被正确填充⚠️ 注意事项:
- 永远不要对
interface{}参数再次取地址(如&responseObject),这会导致运行时 panic;defer resp.Body.Close()必须在client.Do()成功后立即调用,避免因后续解码失败导致连接未释放;- 若
DecodeJsonRequestBody内部封装了json.NewDecoder(...).Decode(...),同样需直接传入responseObject(而非&responseObject);- 此方案兼容所有可被 JSON 反序列化的 Go 结构体,无需额外反射或类型断言,性能与专用函数一致。
总结:利用 Go 的接口抽象能力和
json包对interface{}的原生支持,仅需微小签名调整 + 正确指针传递,即可构建真正通用的 HTTP 请求执行器——简洁、安全、无 runtime 开销。

















