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

如何在Golang Echo框架的分页功能下实现搜索框?

在Golang Echo中为产品分页添加搜索功能

我基于Golang Echo框架实现了产品分页功能(包含handler逻辑、SQL语句、路由配置),但尝试添加搜索框/搜索查询功能时未能成功,请问该如何实现?

现有代码参考

Handler逻辑

// Get page number from path `product/:page`
pageNumber := c.Param("page")
page, err := strconv.Atoi(pageNumber)
if err != nil || page < 1 {
    page = 1
}

// set pageSize to 10 for now
pageSize := 10

offset := (page - 1) * pageSize

// get the rows paginated by offset
rows, err := database.WrapQuery(dbconnections.DBPool, ctx, "GetFromProductPaginatedByOffset", pageSize, offset)
if err != nil {
    loggerWithTrace.Error().Err(err).Caller().Msg("database.WrapQuery error")
}
defer rows.Close()

results := make([]models.Product, 0)

for rows.Next() {
    var data models.Product
    err := rows.Scan(&data.Prefix, &data.Suffix, &data.Usage)
    if err != nil {
        loggerWithTrace.Error().Err(err).Caller().Msg("database.WrapQuery error")
    }
    results = append(results, data)
}

// Get the total item of product 
var totalItems int
err = database.WrapQueryRow(dbconnections.DBPool, ctx, "GetTotalSizeFromProduct").Scan(&totalItems)
if err != nil {
    loggerWithTrace.Error().Err(err).Caller().Msg("database.WrapQueryRow error")
}

totalPages := totalItems / pageSize
nextPage := page + 1
prevPage := page - 1

templateDataMap["DataMap"] = DataMap
templateDataMap["Product"] = results
templateDataMap["Page"] = page
templateDataMap["PageSize"] = pageSize
templateDataMap["TotalItems"] = totalItems
templateDataMap["PageItemCount"] = pageSize
templateDataMap["TotalPages"] = totalPages
templateDataMap["NextPage"] = nextPage
templateDataMap["PrevPage"] = prevPage

return c.Render(http.StatusOK, "product", templateDataMap)

SQL语句

Q_GET_PAGINATION_FROM_PRODUCT = SELECT * FROM PRODUCT where (prefix like '%%%s%%' or "usage" like '%%%s%%') LIMIT $1 OFFSET $2
Q_GET_FROM_PRODUCT_TOTAL_SIZE = SELECT count(*) FROM PRODUCT

路由配置

PATH_PAGINATE_PRODUCT = PATH + "/product/:page"
e.GET(PATH_PAGINATE_PRODUCT, handlers.Product)

解决方案

1. 调整请求参数获取

在Handler中新增搜索关键词的获取逻辑,从URL查询参数中读取:

// 获取页码(原有逻辑保留)
pageNumber := c.Param("page")
page, err := strconv.Atoi(pageNumber)
if err != nil || page < 1 {
    page = 1
}

// 获取搜索关键词,为空时表示无搜索条件
searchQuery := c.QueryParam("q")

2. 修复SQL语句(避免注入+支持搜索)

将原有字符串拼接的SQL改为参数化查询,同时适配搜索条件:

分页查询SQL
-- 替换Q_GET_PAGINATION_FROM_PRODUCT
SELECT * FROM PRODUCT 
WHERE (prefix ILIKE $1 OR "usage" ILIKE $1) 
LIMIT $2 OFFSET $3

注:PostgreSQL用ILIKE实现不区分大小写搜索,MySQL替换为LIKE即可。

总条数查询SQL
-- 替换Q_GET_FROM_PRODUCT_TOTAL_SIZE
SELECT count(*) FROM PRODUCT 
WHERE (prefix ILIKE $1 OR "usage" ILIKE $1)

3. 更新Handler逻辑,整合搜索与分页

修改数据库查询部分,传入搜索参数,并修正分页计算逻辑:

pageSize := 10
offset := (page - 1) * pageSize

// 为搜索关键词添加通配符,适配LIKE查询
searchPattern := "%" + searchQuery + "%"

// 执行带搜索条件的分页查询
rows, err := database.WrapQuery(dbconnections.DBPool, ctx, "GetFromProductPaginatedByOffset", searchPattern, pageSize, offset)
if err != nil {
    loggerWithTrace.Error().Err(err).Caller().Msg("database.WrapQuery error")
    return c.JSON(http.StatusInternalServerError, map[string]string{"error": "查询失败"})
}
defer rows.Close()

results := make([]models.Product, 0)
for rows.Next() {
    var data models.Product
    if err := rows.Scan(&data.Prefix, &data.Suffix, &data.Usage); err != nil {
        loggerWithTrace.Error().Err(err).Caller().Msg("rows.Scan error")
        continue
    }
    results = append(results, data)
}

// 获取符合搜索条件的总条数
var totalItems int
err = database.WrapQueryRow(dbconnections.DBPool, ctx, "GetTotalSizeFromProduct", searchPattern).Scan(&totalItems)
if err != nil {
    loggerWithTrace.Error().Err(err).Caller().Msg("database.WrapQueryRow error")
    return c.JSON(http.StatusInternalServerError, map[string]string{"error": "统计总数失败"})
}

// 修正总页数计算,避免整除丢失最后一页数据
totalPages := (totalItems + pageSize - 1) / pageSize
// 处理上下页边界
nextPage := page + 1
if nextPage > totalPages {
    nextPage = 0 // 用0标记无下一页,模板中可判断隐藏按钮
}
prevPage := page - 1
if prevPage < 1 {
    prevPage = 0 // 用0标记无上一页
}

// 传递搜索关键词到模板,用于回显和分页链接
templateDataMap["SearchQuery"] = searchQuery
templateDataMap["Product"] = results
templateDataMap["Page"] = page
templateDataMap["PageSize"] = pageSize
templateDataMap["TotalItems"] = totalItems
templateDataMap["TotalPages"] = totalPages
templateDataMap["NextPage"] = nextPage
templateDataMap["PrevPage"] = prevPage

return c.Render(http.StatusOK, "product", templateDataMap)

4. 模板层添加搜索表单与带搜索参数的分页链接

在product模板中添加搜索表单,并确保分页导航带上搜索参数:

<!-- 搜索表单 -->
<form method="get" action="/product/{{.Page}}">
    <input type="text" name="q" placeholder="搜索前缀或用途..." value="{{.SearchQuery}}">
    <button type="submit">搜索</button>
</form>

<!-- 分页导航 -->
<div class="pagination">
    {{if gt .PrevPage 0}}
    <a href="/product/{{.PrevPage}}?q={{.SearchQuery}}">上一页</a>
    {{end}}
    <span>第{{.Page}}页 / 共{{.TotalPages}}页</span>
    {{if gt .NextPage 0}}
    <a href="/product/{{.NextPage}}?q={{.SearchQuery}}">下一页</a>
    {{end}}
</div>

5. 路由保持原有配置

无需修改现有路由,原配置即可支持带搜索参数的请求:

PATH_PAGINATE_PRODUCT = PATH + "/product/:page"
e.GET(PATH_PAGINATE_PRODUCT, handlers.Product)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 09:29:54