如何在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
相关产品推荐
相关产品推荐

