Golang通用REST Get函数实现
Golang通用REST Get函数实现 问题:我有一个拥有大量端点的REST服务,并且一直在反复复用代码。
我该如何编写一个通用的GET函数,为其提供所需的对象类型,并让该函数解组响应,将结果以正确的类型返回。
我目前真的不知道如何实现这一点,但如果有人能给我一个入门的指引,我肯定愿意花时间去尝试解决。
谢谢
package main
import (
"fmt"
"net/http"
"io/ioutil"
"encoding/json"
)
var Client *http.Client
type Car struct {
Name string
Colour string
EngineSize int
}
type Fruit struct {
Name string
Calories int
Seedless bool
}
func Main() {
car := Car{}
url := "/blah/blah/cars"
respJson, err := getIt(url, &car)
fmt.Println(respJson.Colour)
url = "/blah/blah/fruits"
fruit := Fruit{}
respJson, err = getIt(url, &fruit)
fmt.Println(respJson.Seedless)
}
func getIt(urlStr string, i interface{}) (interface{}, error) {
req, _ := http.NewRequest("GET", urlStr, nil)
req.Header.Add("Accept", "application/json")
resp, err := Client.Do(req)
if err != nil {
return nil, fmt.Errorf("er......")
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("er......")
}
err = json.Unmarshal(body, &i)
if err != nil {
return nil, fmt.Errorf("can't unmarshal response......")
}
return &i , nil
}
更多关于Golang通用REST Get函数实现的实战教程也可以访问 https://www.itying.com/category-94-b0.html
4 回复
tranman:
我目前真的不知道该如何处理这个问题,但如果有人能给我一个入门的指引,我肯定愿意花时间去尝试解决。
我曾尝试通过创建一个API来解决这个问题,并取得了一些成功。我在这里寻求过一些反馈,但没有得到任何回应。在此处阅读更多关于我的尝试。
要实现一个通用的REST GET函数,可以使用Go的泛型(Go 1.18+)或接口反射。以下是两种实现方式:
方法1:使用泛型(推荐,Go 1.18+)
package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
)
var Client = &http.Client{}
type Car struct {
Name string `json:"name"`
Colour string `json:"colour"`
EngineSize int `json:"engineSize"`
}
type Fruit struct {
Name string `json:"name"`
Calories int `json:"calories"`
Seedless bool `json:"seedless"`
}
func main() {
url := "https://api.example.com/cars/1"
car, err := GetJSON[Car](url)
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
fmt.Printf("Car colour: %s\n", car.Colour)
url = "https://api.example.com/fruits/1"
fruit, err := GetJSON[Fruit](url)
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
fmt.Printf("Fruit seedless: %v\n", fruit.Seedless)
}
func GetJSON[T any](url string) (*T, error) {
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, fmt.Errorf("创建请求失败: %w", err)
}
req.Header.Add("Accept", "application/json")
resp, err := Client.Do(req)
if err != nil {
return nil, fmt.Errorf("请求失败: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("HTTP错误: %s", resp.Status)
}
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("读取响应失败: %w", err)
}
var result T
if err := json.Unmarshal(body, &result); err != nil {
return nil, fmt.Errorf("JSON解析失败: %w", err)
}
return &result, nil
}
方法2:使用接口反射(兼容旧版本)
package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
"reflect"
)
var Client = &http.Client{}
func main() {
url := "https://api.example.com/cars/1"
car := &Car{}
err := GetJSON(url, car)
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
fmt.Printf("Car colour: %s\n", car.Colour)
url = "https://api.example.com/fruits/1"
fruit := &Fruit{}
err = GetJSON(url, fruit)
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
fmt.Printf("Fruit seedless: %v\n", fruit.Seedless)
}
func GetJSON(url string, result interface{}) error {
if reflect.ValueOf(result).Kind() != reflect.Ptr {
return fmt.Errorf("result必须是指针类型")
}
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return fmt.Errorf("创建请求失败: %w", err)
}
req.Header.Add("Accept", "application/json")
resp, err := Client.Do(req)
if err != nil {
return fmt.Errorf("请求失败: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("HTTP错误: %s", resp.Status)
}
body, err := io.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("读取响应失败: %w", err)
}
if err := json.Unmarshal(body, result); err != nil {
return fmt.Errorf("JSON解析失败: %w", err)
}
return nil
}
方法3:带配置选项的通用GET函数
package main
import (
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"time"
)
type ClientOptions struct {
Timeout time.Duration
Headers map[string]string
BaseURL string
}
type RESTClient struct {
client *http.Client
options ClientOptions
}
func NewRESTClient(options ClientOptions) *RESTClient {
return &RESTClient{
client: &http.Client{
Timeout: options.Timeout,
},
options: options,
}
}
func (rc *RESTClient) GetJSON[T any](ctx context.Context, endpoint string) (*T, error) {
url := rc.options.BaseURL + endpoint
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
if err != nil {
return nil, fmt.Errorf("创建请求失败: %w", err)
}
req.Header.Add("Accept", "application/json")
for key, value := range rc.options.Headers {
req.Header.Add(key, value)
}
resp, err := rc.client.Do(req)
if err != nil {
return nil, fmt.Errorf("请求失败: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("HTTP %d: %s", resp.StatusCode, string(body))
}
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("读取响应失败: %w", err)
}
var result T
if err := json.Unmarshal(body, &result); err != nil {
return nil, fmt.Errorf("JSON解析失败: %w", err)
}
return &result, nil
}
// 使用示例
func main() {
client := NewRESTClient(ClientOptions{
Timeout: 30 * time.Second,
Headers: map[string]string{
"Authorization": "Bearer token",
},
BaseURL: "https://api.example.com",
})
ctx := context.Background()
// 获取Car
car, err := client.GetJSON[Car](ctx, "/cars/1")
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
fmt.Printf("Car: %+v\n", car)
// 获取Fruit
fruit, err := client.GetJSON[Fruit](ctx, "/fruits/1")
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
fmt.Printf("Fruit: %+v\n", fruit)
}
泛型方法提供了类型安全且简洁的API,而反射方法兼容旧版本Go。带配置选项的版本更适合生产环境,提供了更好的可配置性和错误处理。


