如何在 Go Echo 框架中为分页页面添加搜索框功能
发布时间 - 2026-02-02 00:00:00 点击率:次本文详解如何在基于 echo 的分页产品列表页面中集成搜索功能,包括安全拼接 like 查询参数、改造 sql 语句、更新路由与处理器逻辑,并避免 sql 注入风险。
在当前的分页实现中,你已通过路径参数 :page 实现了基础分页(如 /product/1),但缺少对用户搜索行为的支持——例如按 prefix 或 usage 模糊查找。直接在 SQL 中使用 '%s%' 占位符并传入原始关键词会导致语法错误或注入漏洞,因此必须重构查询逻辑。
✅ 正确做法:将搜索参数作为独立查询参数传递
首先,修改路由,不再仅依赖路径参数,而是支持带查询参数的 GET 请求(更符合 REST 语义且便于前端表单提交):
// 支持两种访问方式:
// - /product?page=1&search=abc (推荐)
// - /product/1?search=xyz (兼容旧路由,可选)
e.GET("/product", handlers.Product) // 主搜索+分页入口
e.GET("/product/:page", handlers.Product) // 保留原有路径式分页(需解析 query)然后,在 handlers.Product 中统一处理搜索与分页逻辑:
func Product(c echo.Context) error {
ctx := c.Request().Context()
// 1. 解析分页参数(优先从 query,fallback 到 path)
pageStr := c.QueryParam("page")
if pageStr == "" {
pageStr = c.Param("page") // 兼容 /product/1 形式
}
page, err := strconv.Atoi(pageStr)
if err != nil || page < 1 {
page = 1
}
pageSize := 10
offset := (page - 1) * pageSize
// 2. 解析搜索关键词(可为空)
search := strings.TrimSpace(c.QueryParam("search"))
// 3. 构建安全的 LIKE 模式:前后加 %,由 Go 层完成,而非 SQL 拼接
var searchPattern string
if search != "" {
searchPattern = "%" + search + "%"
}
// 4. 执行带搜索条件的分页查询(注意:SQL 中使用 $3、$4 占位符)
rows, err := database.WrapQuery(
dbconnections.DBPool,
ctx,
"GetFromProductPaginatedByOffset",
pageSize,
offset,
searchPattern, // ← 传入已加 % 的 pattern
searchPattern, // ← 同样用于 usage 字段
)
if err != nil {
loggerWithTrace.Error().Err(err).Caller().Msg("paginated query failed")
return echo.NewHTTPError(http.StatusInternalServerError, "failed to fetch products")
}
defer rows.Close()
var results []models.Product
for rows.Next() {
var p models.Product
if err := rows.Scan(&p.Prefix, &p.Suffix, &p.Usage); err != nil {
loggerWithTrace.Error().Err(err).Caller().Msg("scan product row failed")
continue
}
results = append(results, p)
}
// 5. 获取总数量(同样需应用搜索条件)
var totalItems int
err = database.WrapQueryRow(
dbconnections.DBPool,
ctx,
"GetTotalSizeFromProduct",
searchPattern,
searchPattern,
).Scan(&totalItems)
if err != nil {
loggerWithTrace.Error().Err(err).Caller().Msg("count query failed")
}
totalPages := int(math.Ceil(float64(totalItems) / float64(pageSize)))
nextPage := page + 1
if nextPage > totalPages {
nextPage = totalPages
}
prevPage := page - 1
if prevPage < 1 {
prevPage = 1
}
// 6. 渲染模板,透传 search 值以保持搜索状态
templateDataMap := map[string]interface{}{
"Product": results,
"Page": page,
"PageSize": pageSize,
"TotalItems": totalItems,
"TotalPages": totalPages,
"NextPage": nextPage,
"PrevPage": prevPage,
"Search": search, // ← 关键:回填到 input value 中
}
return c.Render(http.StatusOK, "product", templateDataMap)
}? SQL 查询语句更新(关键!)
确保你的 SQL 查询模板(如 Q_GET_PAGINATION_FROM_PRODUCT)使用参数化占位符,禁止字符串拼接:
-- ✅ 正确:使用 $3 和 $4 接收 Go 传入的 '%keyword%' 字符串 SELECT * FROM PRODUCT WHERE (prefix LIKE $3 OR usage LIKE $4) LIMIT $1 OFFSET $2; -- ✅ 对应总数查询: SELECT COUNT(*) FROM PRODUCT WHERE (prefix LIKE $1 OR usage LIKE $2);
⚠️ 注意:不要写成 LIKE '%' || $1 || '%' 或 LIKE '%'+$1+'%' —— 这不仅降低可读性,还可能因数据库方言差异出错;更严重的是,若前端传入恶意内容(如 %'; DROP TABLE...),虽参数化本身防注入,但手动拼接 % 仍易出错。始终由 Go 层构建 pattern,SQL 只做纯匹配。
?️ 前端模板(HTML 示例)
在 product.html 中添加搜索表单,并保持当前搜索词与分页链接同步:
{{ if gt .PrevPage 0 }} ← Prev {{ end }} Page {{ .Page }} of {{ .TotalPages }} {{ if lt .NextPage .TotalPages }} Next → {{ end }}
✅ 总结要点
- 搜索与分页应解耦:用 ?page=2&search=abc 替代硬编码路径;
- LIKE 模式(%xxx%)必须在 Go 层生成,SQL 中仅使用标准 $n 占位符;
- 所有数据库查询(含总数统计)都需一致应用搜索条件;
- 模板中回显 .Search 值,保
障用户体验连续性;
- 避免任何字符串格式化拼接 SQL,坚守参数化查询原则。
这样改造后,你的产品页就拥有了健壮、安全且用户友好的搜索+分页能力。
# word
# html
# 前端
# go
# 处理器
# 编码
# app
# ai
# 路由
# 表单提交
# sql
# echo
# 字符串
# table
# 数据库
# 重构
# 关键词
# 分页
# 表单
# 的是
# 中统
# 两种
# 可选
# 而非
# 只做
# 到第
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
如何在万网ECS上快速搭建专属网站?
深圳网站制作设计招聘,关于服装设计的流行趋势,哪里的资料比较全面?
jQuery validate插件功能与用法详解
如何在IIS7上新建站点并设置安全权限?
Laravel怎么设置路由分组Prefix_Laravel多级路由嵌套与命名空间隔离【步骤】
Laravel如何升级到最新的版本_Laravel版本升级流程与兼容性处理
Laravel如何使用Guzzle调用外部接口_Laravel发起HTTP请求与JSON数据解析【详解】
详解Nginx + Tomcat 反向代理 如何在高效的在一台服务器部署多个站点
Win11怎么设置默认图片查看器_Windows11照片应用关联设置
如何用美橙互联一键搭建多站合一网站?
Laravel怎么配置自定义表前缀_Laravel数据库迁移与Eloquent表名映射【步骤】
🚀拖拽式CMS建站能否实现高效与个性化并存?
如何用ChatGPT准备面试 模拟面试问答与职场话术练习教程
详解MySQL数据库的安装与密码配置
打开php文件提示内存不足_怎么调整php内存限制【解决方案】
Laravel Facade的原理是什么_深入理解Laravel门面及其工作机制
jimdo怎样用html5做选项卡_jimdo选项卡html5实现与切换效果【指南】
Laravel如何构建RESTful API_Laravel标准化API接口开发指南
Android 常见的图片加载框架详细介绍
利用python获取某年中每个月的第一天和最后一天
Laravel怎么写单元测试_PHPUnit在Laravel项目中的基础测试入门
php结合redis实现高并发下的抢购、秒杀功能的实例
Laravel怎么实现验证码功能_Laravel集成验证码库防止机器人注册
如何确认建站备案号应放置的具体位置?
Laravel如何生成和使用数据填充?(Seeder和Factory示例)
Laravel如何实现密码重置功能_Laravel密码找回与重置流程
个人网站制作流程图片大全,个人网站如何注销?
Laravel如何处理跨站请求伪造(CSRF)保护_Laravel表单安全机制与令牌校验
如何制作公司的网站链接,公司想做一个网站,一般需要花多少钱?
Win11怎么关闭透明效果_Windows11辅助功能视觉效果设置
网站建设要注意的标准 促进网站用户好感度!
Swift中switch语句区间和元组模式匹配
谷歌Google入口永久地址_Google搜索引擎官网首页永久入口
Swift开发中switch语句值绑定模式
zabbix利用python脚本发送报警邮件的方法
在Oracle关闭情况下如何修改spfile的参数
UC浏览器如何切换小说阅读源_UC浏览器阅读源切换【方法】
百度浏览器ai对话怎么关 百度浏览器ai聊天窗口隐藏
JavaScript如何实现音频处理_Web Audio API如何工作?
专业企业网站设计制作公司,如何理解商贸企业的统一配送和分销网络建设?
JavaScript如何实现错误处理_try...catch如何捕获异常?
Laravel如何使用Service Provider服务提供者_Laravel依赖注入与容器绑定【深度】
JavaScript实现Fly Bird小游戏
JS碰撞运动实现方法详解
香港服务器租用每月最低只需15元?
如何快速生成橙子建站落地页链接?
Laravel distinct去重查询_Laravel Eloquent去重方法
ChatGPT常用指令模板大全 新手快速上手的万能Prompt合集
Laravel怎么实现软删除SoftDeletes_Laravel模型回收站功能与数据恢复【步骤】
如何在Windows服务器上快速搭建网站?


