为了账号安全,请及时绑定邮箱和手机立即绑定

由于响应结构的微小变化,重复调用 JSON 解码器

由于响应结构的微小变化,重复调用 JSON 解码器

Go
犯罪嫌疑人X 2022-10-31 15:53:57
我已将 Github 和 Google 身份验证系统添加到我的 Web 应用程序中。在这两种情况下,我都希望获得用户电子邮件。我尝试制作一个可以发出 API 请求并获取电子邮件的函数。当Google 返回一个 JSON 对象并在 Github 上返回一个 JSON 数组作为响应时,我遇到了一个问题。 我无法弄清楚如何避免两次调用 JSON 解码器,因为我不能为它们两个使用相同的类型变量。// Sends a request to the API and// authorizes it by setting HTTP header "Authorization" to authHeader valuefunc getUserEmail(endpoint, authHeader, provider string) (string, error) {    var email string       // Store user email here    var client http.Client // Create client so we can modify request headers    // Create a GET request to the endpoint    req, err := http.NewRequest("GET", endpoint, nil)    if err != nil {        return "", err    }    // Authorize the request by using the HTTP header    req.Header.Add("Authorization", authHeader)    // Give the data back as JSON    req.Header.Add("accept", "application/json")    // Send the request    resp, err := client.Do(req)    if err != nil {        fmt.Println("Internet connection or client policy error")        return "", err    }    defer resp.Body.Close()    if provider == "google" {        var response map[string]interface{}        err = json.NewDecoder(resp.Body).Decode(&response)        if err != nil {            fmt.Println("Error occured during decoding access token response")            return "", err        }        email = response["email"].(string)    } else if provider == "github" {        var response []map[string]interface{}        err = json.NewDecoder(resp.Body).Decode(&response)        if err != nil {            fmt.Println("Error occured during decoding access token response")            return "", err        }        email = response[0]["email"].(string)    } else {        return "", errors.New("invalid provider")    }    return email, nil}
查看完整描述

1 回答

?
饮歌长啸

TA贡献1951条经验 获得超3个赞

您可以解组为var response interface{}. 一旦 json 被解组,你可以在type assertion上做一个response检查它是否是[]interface{}或map[string]interface{}从那里去。


var email string

var response interface{}

if err := json.NewDecoder(r.Body).Decode(&response); err != nil {

    return err

}


// If you are sure that the structure of the content of the response,

// given its type, is always what you expect it to be, you can use a

// quick-and-dirty type switch/assertion.

switch v := response.(type) {

case []interface{}:

    email = v[0].(map[string]interface{})["email"].(string)

case map[string]interface{}:

    email = v["email"].(string)

}


// But! If you're not sure, if the APIs don't provide a guarantee,

// then you should guard against panics using the comma-ok idiom

// at every step.

if s, ok := response.([]interface{}); ok && len(s) > 0 {

    if m, ok := s[0].(map[string]interface{}); ok && len(m) > 0 {

        email, _ = m["email"].(string)

    }

} else if m, ok := response.(map[string]interface{}); ok && len(m) > 0 {

    email, _ = m["email"].(string)

}

您还可以根据提供者值预先分配一个指向预期类型的指针,并将请求正文解组到其中,这将减少必要的类型断言的数量,但需要指针解引用。


var email string

var response interface{}

if provider == "google" {

    response = new(map[string]interface{})

} else if provider == "github" {

    response = new([]map[string]interface{})

}

if err := json.NewDecoder(r.Body).Decode(response); err != nil {

    return err

}


switch v := response.(type) {

case *[]map[string]interface{}:

    email = (*v)[0]["email"].(string) // no need to assert the slice element's type

case *map[string]interface{}:

    email = (*v)["email"].(string)

}


查看完整回答
反对 回复 2022-10-31
  • 1 回答
  • 0 关注
  • 86 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信