安全实现搜索接口需校验空参、转换类型、设超时与限流:q为空返回400;page/limit用strconv.parseint校验并处理err;json请求用bodyparser;c.json()前手动设status;加readtimeout和limiter中间件;模板变量名首字母大写。

直接用 ctx.Query() 读参数、c.JSON() 返回结果,就能跑通基础搜索接口;但不处理空参、不校验类型、不设超时,上线后大概率被刷崩或返回错数据。
怎么安全地读取搜索关键词和分页参数
搜索接口最常接收 q(关键词)、page 和 limit(分页)。Fiber 不自动转换类型,ctx.Query("page") 永远返回 string,不能直接传给数据库 offset。
- 用
ctx.Query("q", "")设默认空值,避免nilpanic;空字符串应直接返回空数组或 400 -
page和limit必须显式转整型:page, _ := strconv.Atoi(ctx.Query("page", "1")),但别忽略错误——建议用strconv.ParseInt(..., 10, 64)并检查 err,非法值统一返回400 Bad Request - 别用
ctx.FormValue()读 JSON 请求体里的搜索字段,那是给application/x-www-form-urlencoded准备的;JSON 搜索请走c.BodyParser(&searchReq)
为什么 c.JSON() 返回空数组却状态码是 200
常见现象:关键词不存在时返回 [],但前端发现状态码还是 200,无法区分“没数据”和“成功查到零条”。这不是 bug,是 Fiber 默认行为——c.JSON() 只设 body 和 Content-Type,不改 status。
- 想明确语义,手动设状态:
c.Status(200).JSON(results)或查无结果时用c.Status(204).SendString("") - 如果业务要求“关键词为空返回 400”,必须在解析后立刻判断并提前 return:
if q == "" { return c.Status(400).JSON(fiber.Map{"error": "q is required"}) } - 注意:
c.Status(400).JSON(...)后不加return,后续代码仍会执行,可能覆盖状态码
如何避免 /search?q=xxx 被恶意刷爆
基础接口没限流、没缓存、没超时,一个简单脚本就能拖垮服务。Fiber 本身不带熔断,得靠配置和中间件补足。
- 加请求超时:初始化时设
fiber.Config{ReadTimeout: 5 * time.Second},防止慢查询卡住连接 - 简单限流可用
github.com/gofiber/fiber/v2/middleware/limiter,按 IP 限 100 次/分钟:app.Use(limiter.New(limiter.Config{Max: 100})) - 高频关键词建议加内存缓存(如
github.com/go-redis/redis/v9),但注意缓存穿透:对空结果也缓存 30 秒,避免反复查库 - 别在 handler 里直接拼 SQL——用参数化查询或 ORM,否则
q=abc'; DROP TABLE users;--会生效
模板渲染搜索页时变量总为空
用 c.Render("search", fiber.Map{"Results": results, "Query": q}) 渲染 HTML,但页面里 {{.Results}} 显示为空,实际 results 是非空 slice。
- Fiber 的
fiber.Map键名必须首字母大写才导出:fiber.Map{"Results": results, "Query": q}✅,fiber.Map{"results": results}❌ - 如果
results是 struct 切片,每个 struct 字段也必须大写,且有json:tag(模板和 JSON 共用同一套反射规则) - 开发时启用
engine.SetDebug(true),能实时看到模板编译错误;生产环境关掉,否则每次请求都重读文件
最易被忽略的是上下文生命周期——ctx.Query() 返回的字符串只在 handler 内有效,存到 goroutine 或 map 里再读就是脏数据;真要持久化,得用 utils.CopyString() 或 string([]byte(...)) 拷贝一份。











