Golang实现分页GET请求的方法与技巧
Golang实现分页GET请求的方法与技巧 我在使用 http.Get() 处理分页 API 请求时遇到了困难。
Go 似乎发生了恐慌,但我无法确定原因。 “panic: runtime error: invalid memory address or nil pointer dereference [signal 0xc0000005 code=0x0 addr=0x40 pc=0x64cdc3]”
我知道第一个 GET 请求是成功发出的。问题似乎出在循环过程中。
有没有更简单的方法来实现这个功能?
Orders := OrdersResponse{}
var NextLink string
OrdersCall, _ := http.Get("https://" + APIKey + ":" + APIPass + "@example.myshopify.com/admin/api/2020-04/orders.json?attribution_app_id=123456&limit=250")
Paginate := 1
for Paginate == 1 {
// If we got a NextLink call it.
if strings.Contains(OrdersCall.Header["Link"][0], "next") {
OrdersCall, _ = http.Get(NextLink)
}
OrdResio, _ := ioutil.ReadAll(OrdersCall.Body)
OrdersCall.Body.Close()
OrdJSON := string(OrdResio)
OrdersResp := OrdersResponse{}
json.Unmarshal([]byte(OrdJSON), &OrdersResp)
Orders.Orders = append(Orders.Orders, OrdersResp.Orders...)
if strings.Contains(OrdersCall.Header["Link"][0], "next") {
i := strings.Index(OrdersCall.Header["Link"][0], ">")
NextLink = OrdersCall.Header["Link"][0][1:i]
} else {
Paginate = 0
break
}
}
fmt.Println(len(Orders.Orders))
更多关于Golang实现分页GET请求的方法与技巧的实战教程也可以访问 https://www.itying.com/category-94-b0.html
事实证明,第二次调用时身份验证失败了。这可以归咎于我这边糟糕的错误处理。
更多关于Golang实现分页GET请求的方法与技巧的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
实际上这非常有帮助。我认为 OrdersCall 使用的是从第一个 API 响应返回的任何结构体,但第二个响应不同,因为它包含了额外的头部信息。我猜测第一个和最后一个响应分别没有“上一页”或“下一页”的链接。
我该如何找出 OrdersCall 初始化时 HTTP 客户端正在使用的结构体?或者有没有更简单的方法来处理这个问题?
@Sivan,仅凭这些信息无法确定问题所在。你能提供完整的堆栈跟踪吗?根据那个 panic 中的 addr 值,看起来代码在某个地方访问了一个结构体中的字段,而该字段是 nil 的。要缩小范围,我们需要获取更多上下文信息。
你的代码存在几个关键问题导致了空指针解引用。主要问题在于没有正确处理HTTP响应和错误处理。以下是修复后的实现:
package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
"strings"
"time"
)
type OrdersResponse struct {
Orders []Order `json:"orders"`
}
type Order struct {
ID int64 `json:"id"`
Name string `json:"name"`
// 根据实际API响应添加其他字段
}
func fetchPaginatedOrders(apiKey, apiPass string) ([]Order, error) {
client := &http.Client{
Timeout: 30 * time.Second,
}
baseURL := fmt.Sprintf("https://%s:%s@example.myshopify.com/admin/api/2020-04/orders.json",
apiKey, apiPass)
var allOrders []Order
nextLink := baseURL + "?attribution_app_id=123456&limit=250"
for nextLink != "" {
// 发送请求
req, err := http.NewRequest("GET", nextLink, nil)
if err != nil {
return nil, fmt.Errorf("创建请求失败: %w", err)
}
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("请求失败: %w", err)
}
// 检查响应状态
if resp.StatusCode != http.StatusOK {
resp.Body.Close()
return nil, fmt.Errorf("API返回错误状态: %s", resp.Status)
}
// 读取响应体
body, err := io.ReadAll(resp.Body)
if err != nil {
resp.Body.Close()
return nil, fmt.Errorf("读取响应失败: %w", err)
}
resp.Body.Close()
// 解析JSON
var pageResp OrdersResponse
if err := json.Unmarshal(body, &pageResp); err != nil {
return nil, fmt.Errorf("解析JSON失败: %w", err)
}
// 添加订单到结果集
allOrders = append(allOrders, pageResp.Orders...)
// 检查是否有下一页
nextLink = extractNextLink(resp.Header)
}
return allOrders, nil
}
func extractNextLink(headers http.Header) string {
linkHeader := headers.Get("Link")
if linkHeader == "" {
return ""
}
// Shopify API的Link头格式: <https://...>; rel="next", <https://...>; rel="previous"
links := strings.Split(linkHeader, ",")
for _, link := range links {
parts := strings.Split(strings.TrimSpace(link), ";")
if len(parts) >= 2 && strings.Contains(parts[1], `rel="next"`) {
nextURL := strings.Trim(parts[0], " <>")
return nextURL
}
}
return ""
}
func main() {
apiKey := "your_api_key"
apiPass := "your_api_password"
orders, err := fetchPaginatedOrders(apiKey, apiPass)
if err != nil {
fmt.Printf("获取订单失败: %v\n", err)
return
}
fmt.Printf("总共获取了 %d 个订单\n", len(orders))
}
更简洁的版本使用net/http的自动重定向处理:
func fetchOrdersSimple(apiKey, apiPass string) ([]Order, error) {
client := &http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error {
// 保持认证头信息
req.SetBasicAuth(apiKey, apiPass)
return nil
},
}
url := fmt.Sprintf("https://example.myshopify.com/admin/api/2020-04/orders.json?attribution_app_id=123456&limit=250")
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, err
}
req.SetBasicAuth(apiKey, apiPass)
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
var result OrdersResponse
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
return nil, err
}
return result.Orders, nil
}
关键修复点:
- 使用
http.Client替代http.Get()以便更好地控制请求 - 添加完整的错误处理
- 正确关闭响应体(使用
defer) - 修复Link头的解析逻辑
- 使用
json.NewDecoder直接解析响应体,避免额外的字符串转换

