go http handler中应禁用字符串拼接sql,必须使用database/sql占位符;字段名、排序方向需白名单校验;like模糊搜索由服务端加通配符;sqlx.in需配合rebind并处理nil及分批;空值参数用指针接收并显式判断。

Go HTTP handler里怎么安全拼接查询条件
直接用 fmt.Sprintf 拼 SQL 是最常见也最危险的做法,SQL 注入风险几乎必然存在。哪怕只过滤数字 ID,只要没做类型强转或白名单校验,攻击者就能通过 id=1%20OR%201=1 绕过。
- 所有外部输入必须进
database/sql的占位符机制,例如WHERE name LIKE ?,绝不用字符串拼接 - 字段名(如
name、status)不能来自请求参数,必须硬编码或从预定义白名单中取,比如用 map 映射:validFields := map[string]bool{"name": true, "created_at": true} - 排序字段和方向(
ASC/DESC)同理,禁止直接透传,应转换为枚举或 switch 分支校验 - LIKE 模糊搜索必须手动加通配符,且只在服务端加:
stmt.Query("%" + strings.TrimSpace(q) + "%"),前端不传%
用 sqlx.In 实现 IN 查询的正确姿势
sqlx.In 看起来省事,但容易卡在参数数量限制和类型转换上。PostgreSQL 默认 65535 个参数,MySQL 是 65535(实际受 max_allowed_packet 影响),超了就报 too many SQL variables 错误。
- 调用前先检查切片长度,超过 1000 就分批查,别指望数据库扛得住
-
sqlx.In返回的是(?, ?, ?)和参数列表,必须配合sqlx.Rebind才能适配不同驱动,比如 PostgreSQL 要用$1, $2格式 - 如果传的是
[]int64,没问题;但如果是[]interface{}里混了nil,sqlx.In会 panic,得提前过滤掉 nil 值 - 示例:
query, args, _ := sqlx.In("SELECT * FROM users WHERE id IN (?)", ids) query = db.Rebind(query) rows, _ := db.Queryx(query, args...)
搜索参数解析时怎么避免空值污染 WHERE 条件
用户没填 status,后端却生成了 AND status = '',结果查不到任何数据——这是空字符串、零值、nil 三者没区分清楚的典型表现。
- 用指针接收可选参数:
type SearchReq struct { Status *string `json:"status"` },这样能明确区分“未传”和“传了空字符串” - 对每个字段单独判断:
if req.Status != nil && *req.Status != "",而不是if req.Status != "" - 时间范围字段(
start_time/end_time)建议用*time.Time,并用req.StartTime.After(time.Time{})判断是否有效,因为 time.Time 零值是0001-01-01 - 布尔字段别用
bool类型接收,改用*bool或字符串枚举("true"/"false"),否则 JSON 解析失败时默认为false,逻辑就歪了
为什么不要在 Go 里手写动态 SQL 构建器
自己写个 WhereBuilder 类型,支持链式调用、自动加 AND、去重字段……听起来很酷,但实际维护成本高、易出错、难测,而且掩盖了 SQL 本身的复杂性。
立即学习“go语言免费学习笔记(深入)”;
- 90% 的搜索场景用
if+ 占位符就够了,清晰、可控、debug 友好 - 真要复用逻辑,优先封装成带明确语义的函数,比如
buildUserSearchQuery(req *SearchReq) (string, []interface{}),而不是抽象出 Builder 接口 - ORM(如 GORM)自带的
Where链式调用看似方便,但生成的 SQL 常含冗余括号、隐式类型转换,线上慢查一挖一个准 - 如果项目里已有
sqlc或ent,直接用其生成的类型安全查询方法,比手写动态 SQL 更稳
字段白名单、参数分页、空值判断这三处,最容易在 Code Review 时被跳过,但恰恰是上线后出问题的第一爆点。










