安全搜索需预处理关键词并校验字段:先TrimSpace去空白、ToLower转小写,再遍历非空字段用strings.Contains匹配;并发时各goroutine用局部切片,合并时加锁;复杂规则改用预编译regexp。

直接用 strings.Contains 配合预处理就能撑住大多数后台用户搜索场景;但不加校验、不统一大小写、不处理空字段,接口一上线就会返回错乱结果或 panic。
如何安全地对 User 结构体做关键词模糊匹配
用户搜索不是“有没有这个词”,而是“这个词是否出现在可查字段里”。别把 Name、Email、Phone 写成一长串 || 判断——可读性差,加字段就改逻辑,还容易漏判空指针。
- 定义统一匹配函数,把待查字段收进切片:
fields := []string{u.Name, u.Email, u.Phone} - 提前转小写:
kw := strings.ToLower(strings.TrimSpace(keyword)),避免每次循环都调strings.ToLower - 每个字段先判空再处理:
if f != "" && strings.Contains(strings.ToLower(f), kw),防止strings.ToLower(nil)panic - 中文、emoji、全角空格都 OK,
strings.Contains本身支持 UTF-8,问题出在输入没清洗
为什么不能直接用 r.URL.Query().Get("q") 就开搜
用户输个空格、回车、连续空格,r.URL.Query().Get("q") 会返回非空字符串,导致全量返回。这不是 bug,是没做输入守门。
- 必须用
strings.TrimSpace(q)清首尾空白 - 若结果为空,立刻
http.Error(w, "missing query", http.StatusBadRequest),别往下走 - 不要用
len(q) == 0判空——全角空格、零宽字符会让它失效 - 如果前端传了
q=%20%20,Get解码后就是两个空格,TrimSpace才能捕获
并发搜索多个字段时怎么避免数据竞争
当你要同时查数据库 + 缓存 + 外部 API,结果合并再排序,共享变量不加保护,Results 切片可能丢数据、Total 计数不准。
立即学习“go语言免费学习笔记(深入)”;
- 别用
append直接往全局切片写——即使加了sync.Mutex,也容易因扩容导致旧底层数组被其他 goroutine 继续写 - 每个 goroutine 自己建局部切片,最后用
mutex.Lock()包住一次append合并 - 计数器用
sync/atomic:atomic.AddInt64(&total, int64(len(localResults))),比互斥锁轻量 - 如果结果要分页,别在合并后截断——先各自限流(如每个源最多取 50 条),再合并去重,否则内存和延迟都失控
什么时候该放弃 strings.Contains 改用 regexp
strings.Contains 快、稳、无依赖,但它只能解决“子串存在性”问题。一旦需求变成“邮箱格式”“手机号前缀”“以大写字母开头的昵称”,它就硬扛不住了。
- 用
regexp.Compile预编译正则,别在 handler 里每次Compile——1000 次请求 = 1000 次编译开销 - 简单场景优先用
re.MatchString(line),比re.FindAllString快;要位置信息才用re.FindStringIndex - 忽略大小写写成
(?i)^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$,别自己ToLower全文——破坏原始格式且慢 - 注意 Go 正则引擎是 RE2,不支持 lookbehind,
(?<!@)\w+这种写法会编译失败
最常被跳过的其实是字段空值处理和关键词预清洗——它们不出错,但会让搜索结果不可控。上线前拿 “ ”、“\u3000”(全角空格)、“\x00” 这几类输入压测一遍,比看十遍文档管用。


















