一架梯子,一头程序猿,仰望星空!
Go Fiber教程 > 内容正文

Go Fiber路由


路由

Go Fiber框架的路由(route)的作用就是为不同的url绑定处理函数,用于支持http请求处理,也是所有web框架的请求入口。

路由处理器(函数)

注册与特定的 HTTP 方法绑定的路由。

提示:在有些框架路由处理器,称为控制器,大体上意思一样,就是接收到客户http请求之后交给什么函数(方法)处理。

函数签名:

// HTTP 方法
func (app *App) Get(path string, handlers ...Handler) Router
func (app *App) Head(path string, handlers ...Handler) Router
func (app *App) Post(path string, handlers ...Handler) Router
func (app *App) Put(path string, handlers ...Handler) Router
func (app *App) Delete(path string, handlers ...Handler) Router
func (app *App) Connect(path string, handlers ...Handler) Router
func (app *App) Options(path string, handlers ...Handler) Router
func (app *App) Trace(path string, handlers ...Handler) Router
func (app *App) Patch(path string, handlers ...Handler) Router

// Add 允许您将方法指定为值
func (app *App) Add(method, path string, handlers ...Handler) Router

// All 将路由在所有的 HTTP 方法上注册
// 与 app.Use 几乎相同,但不绑定前缀
func (app *App) All(path string, handlers ...Handler) Router

示例:

// 简单的 GET 处理器
app.Get("/api/list", func(c *fiber.Ctx) error {
  return c.SendString("我是一个 GET 请求!")
})

// 简单的 POST 处理器
app.Post("/api/register", func(c *fiber.Ctx) error {
  return c.SendString("我是一个 POST 请求!")
})

Use 用于加载中间件和前缀url拦截器。这些路由仅匹配每个路径的开头,例如 /john 将匹配 /john/doe, /johnnnnn等。

自定义中间件函数签名:

func (app *App) Use(args ...interface{}) Router

示例:

// 匹配任何请求
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()
})

// 添加多个处理器
app.Use("/api", func(c *fiber.Ctx) error {
  c.Set("X-Custom-Header", random.String(32))
  return c.Next()
}, func(c *fiber.Ctx) error {
  return c.Next()
})

路径

路由路径与请求方法一起,定义了可以进行请求的端点。路由路径可以是字符串字符串模式

基于字符串的路由路径示例

// 此路由路径将匹配根路径的请求,"/":
app.Get("/", func(c *fiber.Ctx) error {
  return c.SendString("根路径")
})

// 此路由路径将匹配到 "/about" 的请求:
app.Get("/about", func(c *fiber.Ctx) error {
  return c.SendString("关于")
})

// 此路由路径将匹配到 "/random.txt" 的请求:
app.Get("/random.txt", func(c *fiber.Ctx) error {
  return c.SendString("随机.txt")
})

与 expressJs 框架一样,路由声明的顺序起着重要作用。当接收到请求时,路由将按照声明的顺序进行检查。

请注意:
请确保在包含固定部分的路由之后编写具有可变参数的路由,以确保这些可变部分不会被错误地匹配,从而导致意外的行为发生。

路由参数

路由参数是路由中的动态元素,可以是命名的或者未命名的片段。这些片段用于捕获URL中指定位置的值。可以使用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 - greedy - not optional
app.Get("/user/+", func(c *fiber.Ctx) error {
    return c.SendString(c.Params("+"))
})

// Optional parameter
app.Get("/user/:name?", func(c *fiber.Ctx) error {
    return c.SendString(c.Params("name"))
})

// Wildcard - greedy - optional
app.Get("/user/*", func(c *fiber.Ctx) error {
    return c.SendString(c.Params("*"))
})

// 该路由路径将匹配请求 "/v1/some/resource/name:customVerb",因为参数字符已被转义
app.Get(`/v1/some/resource/name\:customVerb`, func(c *fiber.Ctx) error {
    return c.SendString("Hello, Community")
})

因为连字符(-)和点号(.)被字面解析,所以可以将它们与路由参数一起使用,达到有用的目的。

所有特殊参数字符也可以使用\进行转义,并丢失它们的值,所以如果你希望在路由中使用它们。建议使用反引号 ```,因为在Go的正则表达式文档中,它们总是使用反引号以确保没有歧义,并且转义字符不会以意外的方式影响正则表达式模式。

// 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
})

此外,路由中可以连续使用多个参数和多个未命名参数字符,比如通配符或加号字符,这大大扩展了路由对用户的可能性。

// 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)

我们已经将路由适配到了Express路由,但目前还不支持正则表达式,因为它们相对较慢。

约束

当与传入的URL发生匹配时,并且URL路径通过参数被划分为路由值时,约束将会执行。该功能在v2.37.0中引入,并受到了.NET Core的启发。

约束并不是对参数的验证。如果约束对参数值无效,则Fiber将返回404处理程序

约束 示例 示例匹配
int :id<int> 123456789, -123456789
bool :active<bool> true, false
guid :id<guid> CD2C1638-1638-72D5-1638-DEADBEEF1638
float :weight<float> 1.234, -1,001.01e8
minLen(value) :username<minLen(4)> 测试(至少4个字符)
maxLen(value) :filename<maxLen(8)> MyFile(最多8个字符)
len(length) :filename<len(12)> somefile.txt(12个字符)
min(value) :age<min(18)> 19(整数值必须至少为18)
max(value) :age<max(120)> 91(整数值不能超过120)
range(min,max) :age<range(18,120)> 91(整数值必须至少为18但不能超过120)
alpha :name<alpha> Rick(字符串必须由一个或多个字母字符组成,不区分大小写,a-z)
datetime :dob<datetime(2006\\\\-01\\\\-02)> 2005-11-01
regex(expression) :date<regex(\\d{4}-\\d{2}-\\d{2})}> 2022-08-27(必须匹配正则表达式)

示例

  • 单个约束
  • 多个约束
  • 正则表达式约束
app.Get("/:test", 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
// 无法找到 GET /1

您可以使用;来添加多个约束。

app.Get("/:test", func(c *fiber.Ctx) error {
  return c.SendString(c.Params("test"))
})

// curl -X GET http://localhost:3000/120000
// 无法找到 GET /120000

// curl -X GET http://localhost:3000/1
// 无法找到 GET /1

// curl -X GET http://localhost:3000/250
// 250

Fiber在注册路由时预编译正则表达式查询,因此对于正则表达式约束没有性能开销。

app.Get(`/:date`, func(c *fiber.Ctx) error {
  return c.SendString(c.Params("date"))
})

// curl -X GET http://localhost:3000/125
// 无法找到 GET /125

// curl -X GET http://localhost:3000/test
// 无法找到 GET /test

// curl -X GET http://localhost:3000/2022-08-27
// 2022-08-27

当使用日期时间约束(*, +, ?, :, /, `,>,;,(,))时,应在使用路由特定字符之前使用\`,以避免错误解析。

可选参数示例

您也可以对可选参数加上约束。

app.Get("/:test?", 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
// 无法找到 GET /7.0

中间件

被设计用来对请求或响应做出改变的函数称为中间件函数Next是一个Fiber路由函数,当被调用时,执行与当前路由匹配next函数。

中间件函数示例

app.Use(func(c *fiber.Ctx) error {
  // 在所有响应中设置自定义头部:
  c.Set("X-Custom-Header", "你好,世界")

  // 进入下一个中间件:
  return c.Next()
})

app.Get("/", func(c *fiber.Ctx) error {
  return c.SendString("你好,世界!")
})

Use方法的路径可以是一个挂载路径或者前缀路径,并且限制中间件只应用于以该路径开头请求的路径。

在运行时添加路由的限制

由于设计和性能考虑,不支持在应用程序启动后动态添加路由。请确保在应用程序启动之前定义好所有的路由。

分组

如果您有许多端点,可以使用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"))
}