由于响应结构的微小变化,重复调用 JSON 解码器
Calling JSON decoder repetitvely because of small change in response structure
我已将 Github 和 Google 身份验证系统添加到我的 Web 应用程序。在这两种情况下,我都希望收到用户电子邮件。我试图制作一个函数,它会发出 API 请求并收到电子邮件。
当 Google 返回一个 JSON 对象 和 Github 一个 JSON 时,我 运行 遇到了一个问题array 作为响应。
我想不出一种方法可以避免两次调用 JSON 解码器,因为我不能为它们使用相同的类型变量。
// Sends a request to the API and
// authorizes it by setting HTTP header "Authorization" to authHeader value
func 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
}
您可以解组为 var response interface{}
。一旦 json 被解组,您可以在 response
上执行 type assertion
以检查它是 []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)
}
您还可以pre-allocate一个指针到基于提供者值的预期类型并将请求正文解组到其中,这将减少类型断言的数量必要但需要 pointer-dereferencing.
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)
}
我已将 Github 和 Google 身份验证系统添加到我的 Web 应用程序。在这两种情况下,我都希望收到用户电子邮件。我试图制作一个函数,它会发出 API 请求并收到电子邮件。
当 Google 返回一个 JSON 对象 和 Github 一个 JSON 时,我 运行 遇到了一个问题array 作为响应。
我想不出一种方法可以避免两次调用 JSON 解码器,因为我不能为它们使用相同的类型变量。
// Sends a request to the API and
// authorizes it by setting HTTP header "Authorization" to authHeader value
func 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
}
您可以解组为 var response interface{}
。一旦 json 被解组,您可以在 response
上执行 type assertion
以检查它是 []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)
}
您还可以pre-allocate一个指针到基于提供者值的预期类型并将请求正文解组到其中,这将减少类型断言的数量必要但需要 pointer-dereferencing.
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)
}