You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Go语言分页列表API全结果获取的抽象实现方案请求

Go 分页列表全结果通用获取实现

前提说明

在Go语言中实现分页列表API的全结果获取归类时遇到困难,寻求通用实现方案。本文为多平台发布。

目标需求

现有如下分页列表获取方法及请求/响应结构,需实现一个通用组件,循环调用该方法直至获取所有结果:

// request 请求对象
type request struct {
    nextToken *string
    pageSize  int
}

// response 响应对象
type response struct {
    nextToken *string
    rows      []interface{}
}

// MAX 分页列表的最大值
var MAX = 20

// ListSomething 分页列表获取示例方法
// 根据指定的pageSize输出最多20个数字序列(0~)
func ListSomething(request request) response {
    var rows []interface{}
    var nextToken *string

    offset, _ := strconv.Atoi(*request.nextToken)
    limit := offset + request.pageSize
    if limit > MAX {
        limit = MAX
        nextToken = nil
    } else {
        limitStr := strconv.Itoa(limit)
        nextToken = &limitStr
    }
    for i := offset; i < limit; i++ {
        rows = append(rows, i)
    }
    return response{
        rows:      rows,
        nextToken: nextToken,
    }
}

需支持类似ListSomethingA、ListSomethingB这类具有共性的分页方法:

  • 请求对象均包含nextToken和pageSize字段
  • 响应对象均包含nextToken和rows字段
  • 调用形式需简洁,例如:
fmt.Println(NewLooper(fetchSomething).Loop())
// 输出: [0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19]

问题现状

已有初步的Looper结构,但无法抽象不同的分页方法、请求及响应对象:

// Looper 分页列表获取类
type Looper struct {
    // 抽象后的方法和请求/响应对象
}

// NewLooper Looper构造函数
func NewLooper(/* 抽象后的方法或请求/响应对象 */) *Looper{
    return &Looper{
        // 抽象后的方法和请求/响应对象
    }
}

// Loop 循环调用直到response.nextToken == nil并返回所有结果
func (l *Looper) Loop() []interface{} {
    firstToken := "0"
    nextToken := &firstToken
    var ans []interface{}

    for {
        response := 抽象方法(/* 抽象请求 */)
        ans = append(ans, response.rows...)
        if response.nextToken == nil {
            break
        }
        nextToken = response.nextToken
    }
    return ans
}

解决方案

利用Go的接口特性抽象分页请求与响应的共性,定义通用的Looper组件:

1. 定义通用接口

import "strconv"

// PaginatorRequest 分页请求通用接口,所有分页请求需实现此接口
type PaginatorRequest interface {
    SetNextToken(token *string) // 设置下一页标识
    SetPageSize(size int)       // 设置每页大小
}

// PaginatorResponse 分页响应通用接口,所有分页响应需实现此接口
type PaginatorResponse interface {
    GetNextToken() *string    // 获取下一页标识
    GetRows() []interface{}   // 获取当前页数据行
}

2. 让现有请求/响应实现接口

为原有的request和response类型添加接口实现:

// 给request类型实现PaginatorRequest接口
func (r *request) SetNextToken(token *string) {
    r.nextToken = token
}

func (r *request) SetPageSize(size int) {
    r.pageSize = size
}

// 给response类型实现PaginatorResponse接口
func (r response) GetNextToken() *string {
    return r.nextToken
}

func (r response) GetRows() []interface{} {
    return r.rows
}

3. 定义通用Looper组件

// Fetcher 分页数据获取函数类型,接收分页请求,返回分页响应
type Fetcher func(req PaginatorRequest) PaginatorResponse

// Looper 通用分页全量获取组件
type Looper struct {
    fetcher   Fetcher       // 分页数据获取函数
    pageSize  int           // 默认每页大小
    initToken *string       // 初始请求标识
}

// Option Looper配置选项
type Option func(*Looper)

// WithPageSize 设置每页大小
func WithPageSize(size int) Option {
    return func(l *Looper) {
        l.pageSize = size
    }
}

// WithInitToken 设置初始请求标识
func WithInitToken(token *string) Option {
    return func(l *Looper) {
        l.initToken = token
    }
}

// NewLooper 创建Looper实例
func NewLooper(fetcher Fetcher, opts ...Option) *Looper {
    // 默认配置
    looper := &Looper{
        fetcher:   fetcher,
        pageSize:  10, // 默认每页10条
        initToken: strPtr("0"), // 默认初始标识为"0"
    }
    // 应用自定义配置
    for _, opt := range opts {
        opt(looper)
    }
    return looper
}

// strPtr 辅助函数:将字符串转为指针
func strPtr(s string) *string {
    return &s
}

// Loop 循环获取所有分页数据
func (l *Looper) Loop() []interface{} {
    var allRows []interface{}
    currentToken := l.initToken

    for {
        // 创建请求实例(需根据实际请求类型调整,此处以原request为例)
        req := &request{}
        req.SetNextToken(currentToken)
        req.SetPageSize(l.pageSize)

        // 调用分页获取函数
        resp := l.fetcher(req)
        allRows = append(allRows, resp.GetRows()...)

        // 检查是否还有下一页
        currentToken = resp.GetNextToken()
        if currentToken == nil {
            break
        }
    }

    return allRows
}

4. 适配原有分页方法

将ListSomething适配为符合Fetcher类型的函数:

func fetchSomething(req PaginatorRequest) PaginatorResponse {
    // 类型断言转换为具体请求类型
    reqImpl, ok := req.(*request)
    if !ok {
        panic("invalid request type for ListSomething")
    }
    return ListSomething(*reqImpl)
}

5. 调用示例

func main() {
    // 使用默认配置创建Looper
    looper1 := NewLooper(fetchSomething)
    fmt.Println(looper1.Loop())
    // 输出: [0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19]

    // 自定义每页大小为5
    looper2 := NewLooper(fetchSomething, WithPageSize(5))
    fmt.Println(looper2.Loop())
    // 输出: [0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19]
}

扩展支持其他分页方法

对于ListSomethingA这类其他分页方法,只需:

  1. 让其requestA实现PaginatorRequest接口
  2. 让其responseA实现PaginatorResponse接口
  3. 编写对应的适配函数,即可复用Looper组件:
func fetchSomethingA(req PaginatorRequest) PaginatorResponse {
    reqImpl, ok := req.(*requestA)
    if !ok {
        panic("invalid request type for ListSomethingA")
    }
    return ListSomethingA(*reqImpl)
}

// 调用
looperA := NewLooper(fetchSomethingA, WithPageSize(20))
fmt.Println(looperA.Loop())

内容的提问来源于stack exchange,提问作者ponyo877

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 21:15:41