You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何消除Go代码中APIData方法的冗余实现?

问题描述

现有如下Go代码,用于调用不同API接口并解析返回的JSON响应:

type Collections struct {
    Object string `json:"object"`
    Data   []struct {
        Object     string      `json:"object"`
        ID         string      `json:"id"`
        Groups     interface{} `json:"groups"`
        ExternalID interface{} `json:"externalId"`
    } `json:"data"`
}
type Members struct {
    Object string `json:"object"`
    Data   []struct {
        Object                string      `json:"object"`
        ID                    string      `json:"id"`
        UserID                string      `json:"userId"`
        Name                  string      `json:"name"`
        Email                 string      `json:"email"`
    } `json:"data"`
}
type Groups struct {
    Object string `json:"object"`
    Data   []struct {
        Object      string `json:"object"`
        ID          string `json:"id"`
        Name        string `json:"name"`
        AccessAll   bool   `json:"accessAll"`
        ExternalID  string `json:"externalId"`
    } `json:"data"`
}


func bwRequestWithBT(uri string) []byte {
    url := endpoint + uri
    // Create a new request using http
    req, err := http.NewRequest("GET", url, nil)
    // Send req using http Client
    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        log.Err(err)
    }
    defer resp.Body.Close()
    body, _ := io.ReadAll(resp.Body)

    return body
}

func (c *Collections) APIData() {
    err := json.Unmarshal(RequestWithBT("/collections"), &c)
    if err != nil {
        log.Error().Err(err).Msg("Error while unmarshalling collection")
    }
}

func (m *Members) APIData() {
    err := json.Unmarshal(RequestWithBT("/members"), &m)
    if err != nil {
        log.Error().Err(err).Msg("Error while unmarshalling members")
    }
}

func (g *Groups) APIData() {
    err := json.Unmarshal(RequestWithBT("/groups"), &g)
    if err != nil {
        log.Error().Err(err).Msg("Error while unmarshalling groups")
    }
}

var members Members
members.APIData()

var groups Groups
groups.APIData()

var collection Collections
collection.APIData()

当前三个结构体各自实现了APIData方法,逻辑重复冗余。希望实现类似如下示例的通用APIData方法:

func (object GenericObject) APIData(uri string){
    err := json.Unmarshal(RequestWithBT(uri), &object)
    if err != nil {
        log.Error().Err(err).Msg("Error while unmarshalling")
    }
}

var members Members
var groups Groups
var collection Collections

members.APIData("/members")
groups.APIData("/groups")
collection.APIData("/collections")

请问这种实现方式是否可行?

解决方案

你给出的GenericObject写法不可行,因为Go是静态类型语言,没有这种"通用对象"的隐式适配机制。不过可以通过两种方式实现通用的APIData逻辑:

方式1:使用泛型函数(Go 1.18+推荐)

直接编写一个泛型函数,接收任意结构体指针和API路径,完成请求与解析:

func FetchAPIData[T any](obj *T, uri string) error {
    body := bwRequestWithBT(uri)
    err := json.Unmarshal(body, obj)
    if err != nil {
        log.Error().Err(err).Msgf("Error while unmarshalling %T", obj)
        return err
    }
    return nil
}

调用方式如下:

var members Members
FetchAPIData(&members, "/members")

var groups Groups
FetchAPIData(&groups, "/groups")

var collection Collections
FetchAPIData(&collection, "/collections")

方式2:利用接口与指针接收者实现方法复用

如果不想用泛型,可以定义一个空接口,然后为所有结构体实现统一签名的方法,内部调用封装好的重复逻辑:

// 辅助函数,封装重复的请求与解析逻辑
func fetchAndUnmarshal(obj interface{}, uri string) error {
    body := bwRequestWithBT(uri)
    err := json.Unmarshal(body, obj)
    if err != nil {
        log.Error().Err(err).Msg("Error while unmarshalling")
        return err
    }
    return nil
}

// 为每个结构体实现APIData方法,内部调用辅助函数
func (c *Collections) APIData(uri string) error {
    return fetchAndUnmarshal(c, uri)
}

func (m *Members) APIData(uri string) error {
    return fetchAndUnmarshal(m, uri)
}

func (g *Groups) APIData(uri string) error {
    return fetchAndUnmarshal(g, uri)
}

调用方式和你期望的一致:

var members Members
members.APIData("/members")

var groups Groups
groups.APIData("/groups")

var collection Collections
collection.APIData("/collections")

补充说明

原代码中bwRequestWithBT存在两处风险:

  1. resp可能为nil时调用resp.Body.Close()会触发panic,需要先判断请求错误;
  2. io.ReadAll的错误被忽略,会隐藏读取响应体的问题。

优化后的bwRequestWithBT:

func bwRequestWithBT(uri string) ([]byte, error) {
    url := endpoint + uri
    req, err := http.NewRequest("GET", url, nil)
    if err != nil {
        return nil, err
    }
    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()
    return io.ReadAll(resp.Body)
}

对应的泛型函数也需要调整为处理错误:

func FetchAPIData[T any](obj *T, uri string) error {
    body, err := bwRequestWithBT(uri)
    if err != nil {
        log.Error().Err(err).Msg("Failed to fetch API data")
        return err
    }
    err = json.Unmarshal(body, obj)
    if err != nil {
        log.Error().Err(err).Msgf("Error while unmarshalling %T", obj)
        return err
    }
    return nil
}

内容的提问来源于stack exchange,提问作者Nagri

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 19:53:12