英文:
Calling JSON decoder repetitvely because of small change in response structure
问题
我已经将Github和Google身份验证系统添加到我的Web应用程序中。我希望在这两种情况下都能获取用户的电子邮件地址。我尝试创建一个函数,该函数将进行API请求并获取电子邮件地址。我遇到了一个问题,即Google返回一个JSON对象,而Github返回一个JSON数组作为响应。我无法找到一种方法来避免调用JSON解码器两次,因为我不能为它们都使用相同类型的变量。
// 发送一个请求到API,并通过将HTTP头“Authorization”设置为authHeader值来授权
func getUserEmail(endpoint, authHeader, provider string) (string, error) {
var email string // 在这里存储用户电子邮件
var client http.Client // 创建客户端以便我们可以修改请求头
// 创建一个GET请求到端点
req, err := http.NewRequest("GET", endpoint, nil)
if err != nil {
return "", err
}
// 通过使用HTTP头来授权请求
req.Header.Add("Authorization", authHeader)
// 将数据返回为JSON
req.Header.Add("accept", "application/json")
// 发送请求
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
}
英文:
I have added Github and Google authentication system to my web application. I am looking to get user email in both of these cases. I tried to make a one function which would make the API request and get the email.<br>
I ran into an issue when Google returned a JSON object and Github a JSON array as a response. <br>
I cant figure out a way how I could avoid calling the JSON decoder twice as I cant have a same type variable for both of them.
// 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
}
答案1
得分: 2
你可以将其解组为var response interface{}
。一旦JSON解组完成,你可以对response
进行类型断言
,以检查它是[]interface{}
还是map[string]interface{}
,然后根据情况进行处理。
var email string
var response interface{}
if err := json.NewDecoder(r.Body).Decode(&response); err != nil {
return err
}
// 如果你确定响应内容的结构,在给定其类型的情况下,总是符合你的预期,你可以使用快速且简单的类型切换/断言。
switch v := response.(type) {
case []interface{}:
email = v[0].(map[string]interface{})["email"].(string)
case map[string]interface{}:
email = v["email"].(string)
}
// 但是!如果你不确定,如果API没有提供保证,那么你应该使用逗号-ok惯用法来防止恐慌。
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) // 不需要断言切片元素的类型
case *map[string]interface{}:
email = (*v)["email"].(string)
}
英文:
You can unmarshal into var response interface{}
. Once the json is unmarshaled you can do a type assertion
on the response
to check if it's []interface{}
or map[string]interface{}
and go from there.
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)
}
You can also pre-allocate a pointer to the expected type based on the provider value and unmarshal the request body into that, this will reduce the number of type assertions necessary but will require 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)
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论