ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Fiber v3 路由完全指南:从 HTTP 方法、路径参数到约束与中间件的进阶实践

Fiber v3 路由完全指南:从 HTTP 方法、路径参数到约束与中间件的进阶实践 Fiber v3 路由完全指南从 HTTP 方法、路径参数到约束与中间件的进阶实践【免费下载链接】fiber⚡️ Express inspired web framework written in Go项目地址: https://gitcode.com/GitHub_Trending/fi/fiber导读本文以 gofiber/fiber v3 的路由子系统为核心系统讲解如何声明与组织 HTTP 端点从一条路由的构成要素HTTP 方法 路径 处理器、Get/Use/All的语义差异到路径参数、通配符与约束Constraints等进阶语法再深入到中间件链、分组/挂载以及处理器形态适配帮助你写出既符合 REST 直觉、又充分发挥 Fiber 底层 radix tree 路由性能的代码。读完本文你将能够把任意合法 URL 设计成精确、可校验的 Fiber 路由并理解每条路由在匹配时的真实行为何时返回 404、何时返回 405、何时命中自动生成的 HEAD 路由。配套说明文档位于 routing.md。一条路由的解剖Anatomy of a RouteFiber 中一条路由把三样东西绑定在一起一个 HTTP 方法由Get、Post等方法辅助函数指定一个路径如/users/:id即 REST 语境下的资源一个或多个处理器当路由命中时运行的 handler或 middleware。app.Get(/users/:id, func(c fiber.Ctx) error { return c.SendString(user: c.Params(id)) })其中Get是路由方法/users/:id是路由路径其中:id是路由参数func(c fiber.Ctx) error是处理器也可以作为中间件。在本文所有较短的示例中app均为fiber.New()返回的*fiber.Apphandler/middleware泛指任意func(c fiber.Ctx) error使用了fmt.Println/fmt.Fprintf的片段还需要额外import fmt。下面是一个完整可运行的最小应用package main import github.com/gofiber/fiber/v3 func main() { app : fiber.New() app.Get(/, func(c fiber.Ctx) error { return c.SendString(Hello, World!) }) app.Listen(:3000) }除了原生的func(fiber.Ctx) errorFiber 还能适配 Express 风格、net/http与fasthttp的处理器完整清单见文末处理器形态。路由处理器Route Handlers注册方法辅助函数把一条路由绑定到特定的 HTTP 方法 上。规范处理器是func(fiber.Ctx) errorFiber 也接受func(fiber.Ctx)并视同其返回了nil。// HTTP methods func (app *App) Get(path string, handler any, handlers ...any) Router func (app *App) Head(path string, handler any, handlers ...any) Router func (app *App) Post(path string, handler any, handlers ...any) Router func (app *App) Put(path string, handler any, handlers ...any) Router func (app *App) Delete(path string, handler any, handlers ...any) Router func (app *App) Connect(path string, handler any, handlers ...any) Router func (app *App) Options(path string, handler any, handlers ...any) Router func (app *App) Trace(path string, handler any, handlers ...any) Router func (app *App) Patch(path string, handler any, handlers ...any) Router func (app *App) Query(path string, handler any, handlers ...any) Router // Add 一次性把同一组 handlers 注册到多个方法上 // handlers 从 handler 开始依序执行。 func (app *App) Add(methods []string, path string, handler any, handlers ...any) Router // All 在所有 HTTP 方法上、以 EXACT精确路径注册路由 // 不同于前缀匹配的 Use。 func (app *App) All(path string, handler any, handlers ...any) Router简单示例// Simple GET handler app.Get(/api/list, func(c fiber.Ctx) error { return c.SendString(Im a GET request!) }) // Simple POST handler app.Post(/api/register, func(c fiber.Ctx) error { return c.SendString(Im a POST request!) })从源码看这些方法辅助函数最终都汇入 Add再由其调用内部的app.register例如Query方法直接转发为app.Add([]string{MethodQuery}, ...)app.go而All则是把配置中全部RequestMethods一次性注册app.go。因此你可以通过 Config.RequestMethods 定制方法集合增删自定义方法同时 Fiber 也内置了QUERY这类带请求体的方法辅助注册。Get vs Use vs All这是 Fiber 路由中最重要的语义分水岭Get以及Post、Put等其他方法辅助在一条精确路径上匹配单一 HTTP 方法All在一条精确路径上匹配所有 HTTP 方法Use注册中间件按前缀匹配、按声明顺序执行并通过调用c.Next()把控制权交给链上的下一环。HelperMethods matchedPath matchingTypical useGet/Post/…oneexacta specific endpointAllevery methodexactone path, any verbUseevery methodprefix (slash boundary); all paths if none givenmiddleware, mounting sub-appsGet单方法精确路径app.Get(/users, func(c fiber.Ctx) error { return c.SendString(GET /users) }) // GET /users - GET /users // POST /users - 405 Method Not Allowed // GET /users/42 - 404 Not Found (exact match only)All全方法仍是精确路径app.All(/ping, func(c fiber.Ctx) error { return c.SendString(c.Method() /ping) }) // GET /ping - GET /ping // POST /ping - POST /ping // DELETE /ping - DELETE /ping // GET /ping/extra - 404 Not Found (still exact path)Use前缀中间件// 空路径 Use匹配任意请求任意方法、任意路径 app.Use(func(c fiber.Ctx) error { c.Set(X-Powered-By, Fiber) return c.Next() }) // 前缀 Use匹配前缀以及斜杠边界以下的任何路径 app.Use(/api, func(c fiber.Ctx) error { return c.Next() }) // 上述空 Use 对所有请求都会执行。以下注释说明哪些请求 // 还会额外命中带 /api 前缀的 Use // /api - 也命中 /api Use (精确前缀) // /api/users - 也命中 /api Use (斜杠边界) // /apiv2 - 仅空 Use (无斜杠边界) // /anything - 仅空 UseUse的实现位于 Use其可接受参数形式非常丰富见下文Use专门小节可以只传处理器也可以传路径 一个或多个处理器甚至直接挂载一个子应用。有序链路Ordered chain同一请求命中多个处理器时它们按声明顺序执行每个处理器必须调用c.Next()才会把控制权交给下一个一旦某个处理器直接 return 而没有调用c.Next()后续整条链路都会被跳过。app.Use(/api, func(c fiber.Ctx) error { fmt.Println(1: auth check) return c.Next() }) app.Use(/api, func(c fiber.Ctx) error { fmt.Println(2: logging) return c.Next() }) app.Get(/api/users, func(c fiber.Ctx) error { fmt.Println(3: handler) return c.SendString(users) }) // GET /api/users 依次打印 // 1: auth check // 2: logging // 3: handler一次注册多个处理器在单次注册中依次罗列多个处理器即可把路由级中间件排在业务处理器之前app.Get(/users/:id, func(c fiber.Ctx) error { // 1: 要求认证 if c.Get(Authorization) { return c.SendStatus(fiber.StatusUnauthorized) // 不调 c.Next() 直接返回链路在此停止 } return c.Next() }, func(c fiber.Ctx) error { // 2: 为下游处理器暂存数据 c.Locals(userID, c.Params(id)) return c.Next() }, func(c fiber.Ctx) error { // 3: 业务处理器读取暂存值 return c.SendString(user c.Locals(userID).(string)) }, ) // GET /users/42 (无 Authorization 头) - 401处理器 2、3 不会执行 // GET /users/42 (带 Authorization) - user 42405 与 404 的区分某路径仅在不同方法上存在路由时返回405 Method Not Allowed某路径完全没有匹配的路由包括被约束拒绝的情况时返回404 Not Found。路径Paths路由路径与 HTTP 方法一起定义一个端点。它可以是纯字符串或模式pattern// 匹配根路由 / app.Get(/, func(c fiber.Ctx) error { return c.SendString(root) }) // 匹配 /about app.Get(/about, func(c fiber.Ctx) error { return c.SendString(about) }) // 匹配 /random.txt点号是字面量 app.Get(/random.txt, func(c fiber.Ctx) error { return c.SendString(random.txt) })声明顺序非常关键与 Express 相同路由按注册顺序匹配先声明先命中。因此应把更具体的静态路径放在带参数的路由之前方法辅助如Get只做精确路径匹配。[!INFO] 为避免意外匹配请把带可变参数的路径放在固定路径之后。参数Parameters路由参数是路径中的动态片段分为具名与匿名两类用于从 URL 中捕获值。获取方式见 ctx.md 中的 Params具名参数用参数名匿名参数用通配符*或加号加下标。字符:、、*用于引入参数在具名片段后追加?使其变为可选是贪婪、必选的通配符至少匹配一个字符*是贪婪、可选的通配符可以什么都不匹配。具名、可选与贪婪参数// 具名参数 app.Get(/user/:name/books/:title, func(c fiber.Ctx) error { fmt.Fprintf(c, %s\n, c.Params(name)) fmt.Fprintf(c, %s\n, c.Params(title)) return nil }) // Plus - 贪婪、必选至少匹配一个字符 app.Get(/user/, func(c fiber.Ctx) error { return c.SendString(c.Params()) }) // 可选具名参数 app.Get(/user/:name?, func(c fiber.Ctx) error { return c.SendString(c.Params(name)) }) // 通配符 - 贪婪、可选可以匹配空 app.Get(/user/*, func(c fiber.Ctx) error { return c.SendString(c.Params(*)) })字面分隔符连字符-、点号.和冒号:在参数之间按字面量处理因此可以与路由参数组合使用Fiber 的路由器能识别这些字符属于字面路径// http://localhost:3000/plantae/prunus.persica app.Get(/plantae/:genus.:species, func(c fiber.Ctx) error { fmt.Fprintf(c, %s.%s\n, c.Params(genus), c.Params(species)) return nil // prunus.persica }) // http://localhost:3000/flights/LAX-SFO app.Get(/flights/:from-:to, func(c fiber.Ctx) error { fmt.Fprintf(c, %s-%s\n, c.Params(from), c.Params(to)) return nil // LAX-SFO }) // http://localhost:3000/shop/product/color:blue/size:xs app.Get(/shop/product/color::color/size::size, func(c fiber.Ctx) error { fmt.Fprintf(c, %s:%s\n, c.Params(color), c.Params(size)) return nil // blue:xs })转义字符用\\转义特殊参数字符即可将其视为字面量这对自定义方法如 Google API Design Guide 中的:customVerb很实用。建议用反引号包裹路由串使转义序列保持清晰// 匹配 /v1/some/resource/name:customVerb因为冒号被转义了 app.Get(/v1/some/resource/name\:customVerb, func(c fiber.Ctx) error { return c.SendString(Hello, Community) })单段多参数在一个路径段内可以串联多个具名或匿名参数包括通配符与加号片段// GET /v1 // Params: sign - , param - v1 app.Get(/:sign:param, handler) // GET /api-v1 // Params: name - v1 app.Get(/api-:name, handler) // GET /customer/v1/cart/proxy // Params: *1 - customer/, *2 - /cart app.Get(/*v1*/proxy, handler) // GET /v1/brand/4/shop/blue/xs // Params: *1 - brand/4, *2 - blue/xs app.Get(/v1/*/shop/*, handler)[!INFO] 相邻具名参数的确定性拆分 与 Express、Gin、Echo 等路由器:param总是消费整个段不同Fiber 允许多个参数共享一个路径段。当相邻具名参数之间没有字面分隔符时路由器无法知道一个参数的结束位置因为模式中没有可供切分的定界符。为保证匹配可确定Fiber 给每个相邻具名参数除最后一个外分配固定长度1 个字符最后一个参数取该段的剩余部分直到下一个定界符或路径结束。这就是为什么/:sign:param匹配/v1得到sign、paramv1而不是signv、param1。如需按多字符切分请在参数之间加入字面分隔符例如用/:sign-:param去匹配/v-1。当一条路由包含多个通配符*或加号片段时用1 起始的下标按符号位置取参通配符用c.Params(*1)、c.Params(*2)加号段用c.Params(1)、c.Params(2)。只有一个通配符/加号时直接用c.Params(*)或c.Params()。Fiber 的路由受 Express 启发但有意不提供正则路由模式——那是出于性能开销考虑。要对参数做正则校验请使用下文的regex()约束。约束Constraints路由约束在 URL 命中、且路径被参数 token 化出路由值后执行。该特性于v2.37.0引入思路源自 .NET Core 的路由约束。[!CAUTION] 约束是匹配规则而非输入校验一旦某个值不满足约束该路由便不匹配Fiber 返回404 Not Found。ConstraintExampleExample matchesint:idint123456789, -123456789bool:activebooltrue,falseguid:idguidCD2C1638-1638-72D5-1638-DEADBEEF1638float:weightfloat1.234, -1001.01e8, 3.14minLen(value):usernameminLen(4)Test (必须至少 4 个字符)maxLen(value):filenamemaxLen(8)MyFile (至多 8 个字符)len(length):filenamelen(12)somefile.txt (恰好 12 个字符)min(value):agemin(18)19 (整数值必须至少为 18)max(value):agemax(120)91 (整数值至多为 120)range(min,max):agerange(18,120)91 (整数值必须在 18 与 120 之间)alpha:namealphaRick (字符串须由字母组成a-z大小写不敏感)datetime:dobdatetime(2006\\-01\\-02)2005-11-01regex(expression):dateregex(\d{4}-\d{2}-\d{2})2022-08-27 (必须匹配正则)单一约束示例app.Get(/:testmin(5), func(c fiber.Ctx) error { return c.SendString(c.Params(test)) }) // curl -X GET http://localhost:3000/12 // 12 // curl -X GET http://localhost:3000/1 // Not Found多约束示例用;分隔多个约束app.Get(/:testmin(100);maxLen(5), func(c fiber.Ctx) error { return c.SendString(c.Params(test)) }) // curl -X GET http://localhost:3000/120000 // Not Found // curl -X GET http://localhost:3000/1 // Not Found // curl -X GET http://localhost:3000/250 // 250正则约束示例Fiber 在注册路由时即预编译正则因此每次请求只需匹配、无需重新编译app.Get(/:dateregex(\d{4}-\d{2}-\d{2}), func(c fiber.Ctx) error { return c.SendString(c.Params(date)) }) // curl -X GET http://localhost:3000/125 // Not Found // curl -X GET http://localhost:3000/test // Not Found // curl -X GET http://localhost:3000/2022-08-27 // 2022-08-27[!CAUTION] 使用 datetime 约束时须为路由结构字符*、、?、:、/、、、;、(、)加\\前缀以避免误解析。可选参数上的约束约束同样可作用于可选参数app.Get(/:testint?, func(c fiber.Ctx) error { return c.SendString(c.Params(test)) }) // curl -X GET http://localhost:3000/42 // 42 // curl -X GET http://localhost:3000/ // // curl -X GET http://localhost:3000/7.0 // Not Found自定义约束通过app.RegisterCustomConstraint可以把自定义约束加入 Fiber约束类型需兼容CustomConstraint接口。源码实现位于 RegisterCustomConstraint它只是把约束实例追加到app.customConstraints切片路由注册与匹配时会据此解析模式中的自定义名称。[!CAUTION] 自定义约束可以覆盖内置约束若自定义约束与内置约束同名则优先使用自定义实现。这为定义路由参数约束提供了更高灵活性。当需要更严格的外部规则例如校验参数是合法 ULID时注册外部约束即可// CustomConstraint 是自定义约束的接口 type CustomConstraint interface { // Name 返回约束名用于约束匹配。 Name() string // Execute 执行约束命中时返回 true。 // param 为待检查的参数值args 为约束参数。 Execute(param string, args ...string) bool }示例type UlidConstraint struct { fiber.CustomConstraint } func (*UlidConstraint) Name() string { return ulid } func (*UlidConstraint) Execute(param string, args ...string) bool { _, err : ulid.Parse(param) return err nil } func main() { app : fiber.New() app.RegisterCustomConstraint(UlidConstraint{}) app.Get(/login/:idulid, func(c fiber.Ctx) error { return c.SendString(...) }) app.Listen(:3000) // /login/01HK7H9ZE5BFMK348CPYP14S0Z - 200 // /login/12345 - 404 }ConstraintHandler 接口除CustomConstraint外Fiber v3 还提供了更强大的ConstraintHandler接口支持在路由注册期做预计算。所有内置约束都实现了该接口// ConstraintHandler 是所有约束必须实现的接口。 type ConstraintHandler interface { // Name 返回路由模式中使用的约束标识符。 Name() string // Execute 校验请求参数值是否符合该约束。 // data 中包含 Analyze() 产生的预类型化约束数据。 Execute(param string, data []any) bool }可选地约束还可实现ConstraintAnalyzer在注册期预处理数据避免每次请求重复解析// ConstraintAnalyzer 是可选接口用于注册期预计算。 type ConstraintAnalyzer interface { // Analyze 在路由注册期预处理约束数据 // 返回存放到 Constraint.Data 的预类型化值。 Analyze(args []string) ([]any, error) }[!NOTE] 现有CustomConstraint实现无需改动即可继续工作——它们会被自动包装以满足ConstraintHandler。同时实现了ConstraintAnalyzer的自定义约束其Analyze方法会在注册期被调用。中间件Middleware凡是改变请求或响应的函数统称中间件函数。c.Next()把控制权交给已匹配链路中的下一个处理器中间件或路由处理器如果某个处理器直接返回而没有调用c.Next()其后的处理器都会被跳过。app.Use(func(c fiber.Ctx) error { // 为所有响应添加自定义响应头 c.Set(X-Custom-Header, Hello, World) // 交给下一个中间件 return c.Next() }) app.Get(/, func(c fiber.Ctx) error { return c.SendString(Hello, World!) })一旦某条路由被命中其处理器便按注册顺序作为同一条链路执行每个处理器通过c.Next()决定是否继续。c.Next()的实现位于 DefaultCtx.Next它会沿着方法栈推进 index 并递归执行后续处理器任何一步 short-circuit不调用c.Next()都会终止整条链。Use的前缀匹配与精确路由的差异、以及多处理器依序执行的方式参见上文 Get vs Use vs All。Use 的各种用法Use把中间件挂载在一个前缀或挂载路径上它会对路径以该前缀开头的任意 HTTP 方法请求执行。前缀要求精确匹配或斜杠边界所以/john能匹配/john与/john/doe但不会匹配/johnnnnn。像:name、:name?、*、这类参数 token 会在边界检查前先展开。不带路径调用时Use匹配每个请求。func (app *App) Use(args ...any) Router // Fiber 会检查 args 以支持如下常见用法 // - app.Use(handler, handlers ...any) // - app.Use(path string, handler, handlers ...any) // - app.Use(paths []string, handler, handlers ...any) // - app.Use(path string, subApp *App)每个 handler 参数可以独立地是带/不带 error 返回的 Fiber 处理器、Express 风格回调、net/http处理器或其他受支持的形态含返回 error 的 fasthttp 回调。// 匹配任意请求 app.Use(func(c fiber.Ctx) error { return c.Next() }) // 匹配以 /api 开头的请求 app.Use(/api, func(c fiber.Ctx) error { return c.Next() }) // 匹配以 /api 或 /home 开头的请求多前缀支持 app.Use([]string{/api, /home}, func(c fiber.Ctx) error { return c.Next() }) // 挂载多个处理器按顺序执行每个都必须调用 c.Next() 才会继续 app.Use(/api, func(c fiber.Ctx) error { c.Set(X-Custom-Header, value) return c.Next() }, func(c fiber.Ctx) error { return c.Next() }) // 挂载子应用 app.Use(/api, api)最后一个形态正是 Fiber子应用挂载的入口配合 mount.go 中的Mount机制可以在一个端口上把多个独立*fiber.App按路径前缀组装起来。挂载时底层会为子应用建立独立的栈并在前缀处桥接router.go 的注册逻辑会识别route.mount标记因此子应用内部的Group、域名路由与自动 HEAD 逻辑都能独立运作。运行时增删路由[!CAUTION]强烈建议在应用启动前把全部路由定义完毕。虽然可以用RebuildTree、RemoveRoute、RemoveRouteByName与RemoveRouteFunc在运行时修改路由表但这些操作非线程安全且开销很高只应在开发环境少量使用。分组Grouping端点很多时可以用Group组织路由func main() { app : fiber.New() api : app.Group(/api, middleware) // /api v1 : api.Group(/v1, middleware) // /api/v1 v1.Get(/list, handler) // /api/v1/list v1.Get(/user, handler) // /api/v1/user v2 : api.Group(/v2, middleware) // /api/v2 v2.Get(/list, handler) // /api/v2/list v2.Get(/user, handler) // /api/v2/user log.Fatal(app.Listen(:3000)) }从源码看Groupapp.go返回的是实现了Router接口的*Group组前缀会在内部与子路由路径拼接getGroupPath见 helpers.go组级中间件通过一次methodUse注册挂在组前缀下。分组更多细节见 Grouping Guide。RouteRoute是Group的简写它在一个回调内部、以公共前缀划定一组路由并支持可选的名字前缀。app.Route(/api/v1, func(r fiber.Router) { r.Get(/users, handler).Name(users) // /api/v1/users (name: v1.users) r.Post(/users, handler).Name(create) // /api/v1/users (name: v1.create) }, v1.)Route 的实现先以app.Group(prefix)建组若有 name 参数则对组调用group.Name(name[0])再把回调执行于该组上。路由命名后可通过 GetRoute 或app.GetRoutes()按名检索便于在RebuildTree/RemoveRouteByName场景使用。RouteChain当多个 HTTP 方法共享同一路径时RouteChain让你只声明一次路径、再链式串联各动词处理器。链内的All在该路径的动词处理器之前运行充当路由级中间件app.RouteChain(/events). All(func(c fiber.Ctx) error { return c.Next() }). // route-local middleware Get(func(c fiber.Ctx) error { return c.SendString(GET /events) }). Post(func(c fiber.Ctx) error { return c.SendString(POST /events) })[!NOTE] 在链中All注册的是前缀匹配的中间件类似Use并非精确路径的app.All因此它对链路径的子路径同样生效。这一行为在 Registering.All 中体现得很直接它以methodUse作为方法集合去调用app.register与精确方法注册Registering.Add走的是不同通道。如何选择注册方式单个端点用Get/Post/…一条路径上的固定方法集合用Add一条路径上的多种方法流畅式用RouteChain共享前缀的众多路径用Group或Route。自动 HEAD 路由Fiber 会为每一条GET路由自动注册一条HEAD路由。生成的处理器链镜像 GET 链因此HEAD请求会复用相同的中间件、状态码与响应头只是响应体被抑制。app : fiber.New() app.Get(/users/:id, func(c fiber.Ctx) error { c.Set(X-User, c.Params(id)) return c.SendStatus(fiber.StatusOK) }) // HEAD /users/:id 现在返回相同的头与状态码但无响应体。即使启用了自动注册你仍可显式注册专用的HEAD处理器Fiber 会替换生成的版本、使你的实现胜出app.Head(/users/:id, func(c fiber.Ctx) error { return c.SendStatus(fiber.StatusNoContent) })要全局关闭该特性可用DisableHeadAutoRegister启动应用handler : func(c fiber.Ctx) error { c.Set(X-User, c.Params(id)) return c.SendStatus(fiber.StatusOK) } app : fiber.New(fiber.Config{DisableHeadAutoRegister: true}) app.Get(/users/:id, handler) // HEAD /users/:id 现在返回 405除非你手动注册。从源码看自动 HEAD 是在应用启动Listen内部时由ensureAutoHeadRoutesrouter.go完成的它扫描 GET 栈为每个 GET 路由复制出一条Method MethodHead、autoHead true的伴生路由并用autoHeadKey去重当显式注册 HEAD 路由时会先通过pruneAutoHeadRouteLocked清除同名自动版本router.go。配置开关位于 Config.DisableHeadAutoRegister。自动生成的 HEAD 路由参与所有路由器作用域Group层级、挂载的子应用、带参数与通配符的路径、静态文件辅助函数均不例外。它们也会出现在app.Stack()之类的路由列表中因此工具能看到 GET 与 HEAD 两条记录。处理器形态Handler TypesFiber 通过适配器把多种处理器形态统一转换成原生的func(fiber.Ctx) error回调。支持 17 种形态按如下分组任何其他签名都会在注册路由时被拒绝。这让 Fiber 风格处理器、Express 风格回调乃至net/http、fasthttp函数可以混用。Fiber 原生处理器形态 1-2形态 1.fiber.Handler—— 规范形式func(fiber.Ctx) error。形态 2.func(fiber.Ctx)—— Fiber 执行该函数并视为其返回nil。Express 风格请求处理器形态 3-12形态 3.func(fiber.Req, fiber.Res) error形态 4.func(fiber.Req, fiber.Res)形态 5.func(fiber.Req, fiber.Res, func() error) error形态 6.func(fiber.Req, fiber.Res, func() error)形态 7.func(fiber.Req, fiber.Res, func()) error形态 8.func(fiber.Req, fiber.Res, func())形态 9.func(fiber.Req, fiber.Res, func(error))形态 10.func(fiber.Req, fiber.Res, func(error)) error形态 11.func(fiber.Req, fiber.Res, func(error) error)形态 12.func(fiber.Req, fiber.Res, func(error) error) error当你的签名接受next回调时适配器会注入一个。Fiber 会把c.Next()的下游错误通过包装层向上传播因此返回这些错误是可选的。如果你从不调用注入的next函数处理器链就此停止——这与 Express 语义一致。若next回调接受error参数next(nil)继续链路传入非 nil error 则携带该错误短路退出。若处理器本身返回 errorFiber 会优先该值而不是记录下来的nexterror。Fiber没有Express 风格的四参错误处理器func(err, req, res, next)非 nil 错误会传播到应用中央的ErrorHandler统一处理。net/http 处理器形态 13-15形态 13.http.HandlerFunc形态 14.http.Handler形态 15.func(http.ResponseWriter, *http.Request)[!CAUTION] 兼容层开销 Fiber 通过fasthttpadaptor适配这些处理器。它们拿不到fiber.Ctx、无法调用c.Next()因此总是终止处理器链。该兼容层也比运行原生 Fiber 处理器多出额外开销能用原生形态就尽量别用它。fasthttp 处理器形态 16-17形态 16.fasthttp.RequestHandler形态 17.func(*fasthttp.RequestCtx) errorfasthttp 处理器能完整访问底层的fasthttp.RequestCtx应当自行管理响应。func(*fasthttp.RequestCtx) error变体返回的 error 会被 Fiber 传播但除此之外 Fiber 不检查该 context 的状态。// 无需手工适配即可复用已有 net/http 处理器 httpHandler : http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusNoContent) }) app.Get(/foo, httpHandler) // 用 fiber.Req / fiber.Res 对齐 Express 风格处理器 // 对中间件与路由同样适用 app.Use(func(req fiber.Req, res fiber.Res, next func() error) error { if req.IP() 192.168.1.254 { return res.SendStatus(fiber.StatusForbidden) } return next() }) app.Get(/express, func(req fiber.Req, res fiber.Res) error { return res.SendString(Hello from Express-style handlers!) }) // 直接挂载 fasthttp.RequestHandler形态 16 app.Get(/bar, func(ctx *fasthttp.RequestCtx) { ctx.SetStatusCode(fiber.StatusAccepted) }) // ……或返回 error 的变体形态 17 app.Get(/baz, func(ctx *fasthttp.RequestCtx) error { ctx.SetStatusCode(fiber.StatusAccepted) return nil })小结把路由当作一套完整的设计语言梳理完 Fiber 的路由体系可以看到它其实是一整套环环相扣的设计语言选择注册入口——精确端点用方法辅助/All公共前缀用Group/Route单路径多方法用RouteChain/Add全局或前缀中间件用Use设计路径模式——:具名可?可选、贪婪必选、*贪婪可选支持字面分隔符与\\转义并理解相邻具名参数取 1 字符 末位余量的确定性拆分规则收紧匹配边界——用int、range(18,120)、regex(...)等约束把匹配做精确制导需要时用RegisterCustomConstraint/ConstraintHandler扩展约束失败返回 404方法不匹配返回 405需牢记编排执行链——依赖c.Next()的中间件顺序、路由级多处理器短路语义、自动 HEAD 伴生路由复用与扩展——Group/Route组织层级Use挂载子应用adapter 兼容 Express/net/http/fasthttp处理器。如需交互式验证路由模式、注册顺序与约束的真实匹配行为可配合 route-matcher 工具对实际请求进行测试。深入理解本文内容之后你便可以把任何 URL 空间清晰、高效、可验证地映射为 Fiber 路由表。【免费下载链接】fiber⚡️ Express inspired web framework written in Go项目地址: https://gitcode.com/GitHub_Trending/fi/fiber创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表