Fiber路由需路径、方法、参数声明、StrictRouting四者严格对齐,缺一即404/405;/users与/users/因StrictRouting=true被视为不同路由,正确解法是在所有路由前加中间件将带尾斜杠路径301重定向至无斜杠版本。

直接说结论:Fiber 的路由不是“写对就能跑”,而是“路径、方法、参数声明、StrictRouting 四者必须同时对齐,缺一不可”。写错一个地方,404 或 405 就立刻出现,且错误提示不告诉你哪错了。
为什么 /users 和 /users/ 会 404?StrictRouting 不是 bug 是开关
Fiber 默认开启 StrictRouting: true,这意味着 /users 和 /users/ 是两条完全不同的路由。你只注册了 app.Get("/users", h),那访问 /users/ 必然 404——不是框架抽风,是你没注册它。
- 别关
StrictRouting: false:它会让/users/:id和/users/冲突,比如/users/123可能被/users拦截,c.Params("id")拿不到值 - 正确做法是加中间件统一重定向:在所有路由注册前放一段逻辑,检测路径是否以
/结尾且长度 > 1,是则用c.Redirect(strings.TrimSuffix(path, "/"), fiber.StatusMovedPermanently)跳转 - 注意保留 query string:上面的
c.Redirect()不带参数时会丢掉?page=2,要用c.Request().URI().FullURI()拆解重建 URL
PUT/DELETE 请求总 405?先看 Network 面板里 Method 到底是不是真 PUT
浏览器原生 <form> 标签只支持 GET 和 POST,写 method="put" 会被忽略,退化成 GET。这不是 Fiber 的问题,是 HTML 规范限制。
开箱即用的技能链路由引擎。13 条预定义链覆盖搜索、开发、审查、MLOps、法律、创意等场景,三层路由架构(触发词→SAD反馈→DAG编排),recall@10=96.97%。配置驱动(chains.yaml),零代码扩展。pip install skill-weave-chains 一键安装。
- 真 PUT/DELETE 必须用 JS 发送:
fetch("/api/user/123", { method: "PUT", body: JSON.stringify(...) }) - Fiber 不做方法模拟,也不读
_method隐藏字段,所以不用配HiddenHttpMethodFilter这类东西 - 排查 405:打开浏览器 Network 面板,点开请求,看 Request Method 列显示的是不是
PUT,而不是只看代码里写的字符串 - 跨域带
Authorization头?浏览器会先发OPTIONS预检——没配cors.New()中间件,就卡在预检失败,表现为只有 OPTIONS 请求且状态码是 404/405
c.Params("id") 总是空?路径参数必须显式声明,不能靠猜
c.Params("id") 不是从 URL 里“自动提取”的,它只从你路由定义中明确写出的占位符里取值。没声明,就永远返回空字符串。
- 正确写法:
app.Get("/user/:id", h)→c.Params("id")才能拿到"123" - 错误写法:
app.Get("/user/{id}", h)或app.Get("/user/:id/", h)→ 直接 404,Fiber 不识别花括号或结尾冒号 - 通配符
*path只能放在路径末尾:app.Get("/files/*path", h)→c.Params("*path")得到"a/b/c";但/files/:id/*path会 panic - 别和
c.QueryParam("id")混:前者来自路径/user/123,后者来自查询串/user?id=123,内存地址不同,互不影响
Body 解析失败、JSON 报 invalid、表单取不到值?Content-Type 和方法必须匹配
Fiber 不自动“猜”你传的是什么格式。c.BodyParser() 默认只处理 application/json,c.FormValue() 只处理 application/x-www-form-urlencoded 或 multipart/form-data,强行混用就丢数据。
- 前端发 JSON:确保请求头有
Content-Type: application/json,后端用c.BodyParser(&u),结构体字段必须带json:"name"tag - 前端用
FormData:别用c.BodyParser(),改用c.FormValue("field")或c.FormFile("file") -
c.Body()只能调一次:它是底层缓冲区引用,第二次调返回空;需要复用时先body := c.Body()拷贝,再用bytes.NewReader(body) - 高并发下别存
c.Params("id")返回值:它是内部字节切片引用,goroutine 里用可能读到下一个请求的数据
最常被忽略的点:Fiber 的 Ctx 是从 sync.Pool 复用的,不是每次新建。所有 c.Params()、c.QueryParam()、c.Body() 返回的都是内部缓冲区引用,不是新分配字符串。存它、传它进 goroutine、塞进 map 当 key,不出三天就出竞态。


















