Маршрутизация

Роль маршрута в фреймворке Go Fiber заключается в привязке различных URL-адресов к обработчикам функций для поддержки обработки HTTP-запросов. Он служит входной точкой для всех запросов веб-фреймворка.

Обработчики маршрутов (Функции)

Регистрируют маршруты, привязанные к определенным 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 allows you to specify methods as values
func (app *App) Add(method, path string, handlers ...Handler) Router

// All registers the route on all HTTP methods
// Подобно 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
})
// Плюс - жадный - необязательный
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("*"))
})

// Этот путь маршрута будет соответствовать запросу "/v1/some/resource/name:customVerb", потому что символы параметра были экранированы
app.Get(`/v1/some/resource/name\:customVerb`, func(c *fiber.Ctx) error {
    return c.SendString("Привет, команда")
})

Поскольку дефисы (-) и точки (.) интерпретируются буквально, их можно использовать вместе с параметрами маршрута для достижения полезных целей.

Все специальные символы параметров также могут быть экранированы с помощью \, и их значения будут игнорироваться, поэтому, если вы хотите использовать их в маршруте, рекомендуется использовать обратные кавычки ``, поскольку в документации по регулярным выражениям 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 // синий: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 123456789, -123456789
bool :active true, false
guid :id CD2C1638-1638-72D5-1638-DEADBEEF1638
float :weight 1.234, -1,001.01e8
minLen(value) :username<minLen(4)> test (не менее 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 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 предварительно компилирует запросы с регулярными выражениями при регистрации маршрутов, поэтому нет накладных расходов на производительность для ограничений с регулярными выражениями.

```go
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


> При использовании ограничений даты-времени (`*`,`+`,`?`,`:`,`/`,``,``,`>`,';`,`(`,`)`), используйте `\\` перед специальными символами в маршруте, чтобы избежать неправильной интерпретации.

**Пример опционального параметра**

Вы также можете добавить ограничения к опциональным параметрам.

```go
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

Промежуточное программное обеспечение

Функции, предназначенные для обработки запросов или ответов, называются функциями промежуточного программного обеспечения (middleware functions). 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 может быть путем монтирования (mount path) или префиксным путем (prefix path), и он ограничивает применение промежуточного программного обеспечения только к запросам с путями, начинающимися с этого пути.

Добавление ограничений маршрута во время выполнения

В связи с конструктивными и производственными соображениями динамическое добавление маршрутов после запуска приложения не поддерживается. Пожалуйста, убедитесь, что все маршруты определены до запуска приложения.

Группировка

Если у вас много конечных точек, вы можете использовать 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"))
}