---
title: "Маршрутизация: методы и пути"
description: "Маршрутизация в Fiber: методы Get, Post, Put, Delete, параметры :id, опциональные и wildcard-пути, порядок срабатывания маршрутов и коды 404/405 на примерах."
course: "Fiber: экспресс-подобный веб-фреймворк на Fasthttp"
url: "https://golangmaster.ru/courses/fiber/lessons/03-marshrutizaciya/"
published: "2026-09-19"
updated: "2026-09-21"
minutes: 20
---

# Маршрутизация: методы и пути

*Курс «Fiber: экспресс-подобный веб-фреймворк на Fasthttp», урок 3 из 25. ~20 минут. Обновлено 21.09.2026. Источник: https://golangmaster.ru/courses/fiber/lessons/03-marshrutizaciya/*

Маршруты в Fiber — это короткие методы app.Get, app.Post, app.Delete и параметры вида :id. Разберём маршрутизацию Fiber построчно: методы HTTP, несколько параметров в одном пути, опциональные и wildcard-сегменты, порядок срабатывания маршрутов и поведение при 404/405.

### Пять эндпоинтов сервиса заметок

Продолжим сервис заметок из прошлого урока. Для CRUD нужно пять эндпоинтов: список, одна заметка, создание, обновление, удаление. В стандартной библиотеке со времён Go 1.22 это mux.HandleFunc("GET /notes/{id}", ...) — работает, но всё остальное (группы, middleware на пачку маршрутов, аккуратные 405) вы собираете сами. В Fiber каждый эндпоинт — один вызов с понятным именем метода, а параметры пути достаются из контекста одной строкой.

Справедливости ради: если вам нужен только маршрутизатор без остального, chi или встроенный mux закроют вопрос. Ценность Fiber начинается, когда к маршрутам добавляются middleware, валидация и унифицированные ответы — но фундамент всё равно здесь, поэтому роутинг разберём досконально.

### CRUD-заготовка на Fiber

```go
package main

import (
        "log"

        "github.com/gofiber/fiber/v3"
)

func main() {
        app := fiber.New()

        app.Get("/notes", func(c fiber.Ctx) error {
                return c.SendString("Список заметок")
        })

        app.Get("/notes/:id", func(c fiber.Ctx) error {
                return c.SendString("Заметка " + c.Params("id"))
        })

        app.Post("/notes", func(c fiber.Ctx) error {
                return c.SendStatus(fiber.StatusCreated)
        })

        app.Put("/notes/:id", func(c fiber.Ctx) error {
                return c.SendString("Обновляем заметку " + c.Params("id"))
        })

        app.Delete("/notes/:id", func(c fiber.Ctx) error {
                return c.SendStatus(fiber.StatusNoContent)
        })

        log.Fatal(app.Listen(":3000"))
}
```

Разбор по строкам. app.Get("/notes") отвечает за список, app.Get("/notes/:id") — за одну заметку: двоеточие объявляет параметр пути, его значение достаётся через c.Params("id"). app.Post("/notes") возвращает 201 через c.SendStatus(fiber.StatusCreated) — коды статусов в Fiber объявлены константами вроде StatusCreated и StatusNoContent. app.Put и app.Delete — обновление и удаление; удаление отвечает 204, телесного контента у него нет. Пять вызовов — пять эндпоинтов, никаких ручных switch по путям.

### Проверьте себя

**Вопрос:** Какой ответ получит клиент после POST /notes к серверу из примера?

- 200 OK с пустым телом
- ✅ 201 Created и в теле «Created»
- 201 Created и JSON новой заметки
- 204 No Content

SendStatus(fiber.StatusCreated) ставит код 201 и, поскольку тело ещё пустое, записывает в него название статуса — «Created». JSON-заметку хендлер не возвращал: c.JSON появится в уроке про JSON.

### Параметры пути: :id, дефолты и несколько сегментов

Двоеточие в маршруте Fiber объявляет именованный параметр: он матчит ровно один сегмент пути. Значение достаётся методом c.Params("id") — всегда строка. Числа получаются через strconv.Atoi с проверкой ошибки, но об этом — в следующем уроке про параметры.

Параметров может быть несколько, и у Params есть приятная мелочь — значение по умолчанию вторым аргументом: c.Params("name", "гость"). А если добавить к сегменту вопросительный знак — :name? — параметр становится опциональным: маршрут сработает и без него. Это удобно для хвостов вроде /profile/:section? — страница профиля и её разделы живут в одном маршруте.

### Два параметра и опциональный сегмент

```go
app.Get("/users/:userId/posts/:postId", func(c fiber.Ctx) error {
        return c.SendString("user=" + c.Params("userId") +
                " post=" + c.Params("postId"))
})

app.Get("/hello/:name?", func(c fiber.Ctx) error {
        return c.SendString("Привет, " + c.Params("name", "гость"))
})
```

В /users/:userId/posts/:postId два параметра: запрос GET /users/42/posts/7 вернёт «user=42 post=7». Имена в маршруте и ключи в Params должны совпадать — опечатка здесь не падает при компиляции, а возвращает пустую строку, так что проверяйте внимательно. Маршрут /hello/:name? благодаря вопросительному знаку сматчит и /hello — тогда Params вернёт дефолт «гость», — и /hello/bob, где name равен «bob».

### Опциональность имеет значение

**Вопрос:** Зарегистрирован только маршрут app.Get("/users/:id", ...). Что получит клиент при запросе GET /users без идентификатора?

- 200 OK с пустым id
- ✅ 404 Not Found — маршрут не сматчился
- 405 Method Not Allowed
- Бесконечное ожидание ответа

Именованный параметр обязан заполниться хотя бы одним символом, поэтому /users не матчится — клиент получает 404. Нужен и вариант без id: сделайте параметр опциональным, /users/:id?, и добавьте дефолт в Params.

### Wildcard и плюс: жадные маршруты

Звезда в конце пути — жадный и опциональный остаток: app.Get("/files/*") сматчит и /files/, и /files/docs/report.pdf. Всё, что пришло после префикса, включая слэши, достаётся через c.Params("*"). Плюс ведёт себя почти так же, но обязателен: /files/+ не сматчит пустой остаток /files/. Это отличие часто всплывает, когда нужно, чтобы корень префикса отдавал отдельный ответ.

Для сравнения: в net/http с Go 1.22 аналог звезды записывается как {path...}. Синтаксис другой, идея та же — забрать весь хвост пути одним параметром.

### Забираем весь хвост пути

```go
app.Get("/files/*", func(c fiber.Ctx) error {
        return c.SendString("Остаток пути: " + c.Params("*"))
})

// GET /files/docs/report.pdf
// -> "Остаток пути: docs/report.pdf"
```

Звезда поглощает остаток пути любой вложенности. Обратите внимание: ведущий слэш после /files в значение не попадает — вы получаете docs/report.pdf, а не /docs/report.pdf. На практике по этому хвосту обычно ищут файл на диске; помните про проверку пути, чтобы клиент не выбрался за пределы папки — в Fiber для раздачи файлов есть готовый app.Static, к нему отдельный урок.

### Что вернёт сервер на запрос GET /shop/hello?

```go
app.Get("/shop/hello", func(c fiber.Ctx) error {
        return c.SendString("Статический")
})

app.Get("/shop/:name", func(c fiber.Ctx) error {
        return c.SendString("Параметр: " + c.Params("name"))
})
```

**Что выведет программа?**

- ✅ «Статический» — отработал первый маршрут
- «Параметр: hello»
- 404 Not Found
- 500 Internal Server Error

Fiber перебирает маршруты в порядке регистрации, и побеждает первый сматчившийся. /shop/hello зарегистрирован раньше, поэтому запрос достаётся ему. Поменяйте две строки местами — и слово hello навсегда превратится в параметр name.

### Порядок регистрации: первый выигрывает

Правило из прошлого примера стоит зафиксировать отдельно, потому что оно тихо ломает большие API. Fiber проверяет маршруты в том порядке, в каком вы их зарегистрировали. Конкретный путь всегда выигрывает у параметра, только если он объявлен раньше. Отсюда дисциплина больших роутеров: сначала точные пути, потом параметризованные, wildcard — последними.

Второй нюанс — регистрация дублей. Дважды объявленный app.Get("/notes") не упадёт при старте, но по маршруту пройдёт только первая запись. Это редко осознанный выбор, чаще забытый дубль после рефакторинга, — я такое ловил дважды и оба раза дольше, чем хотелось бы.

### app.All, app.Add и коды 404/405

Не всегда один маршрут — один метод. app.All("/health", ...) регистрирует хендлер на всех HTTP-методах ровно по этому пути — в отличие от app.Use, который работает по префиксу и разбирался бы с middleware. app.Add([]string{fiber.MethodGet, fiber.MethodHead}, "/ping", ...) вешает хендлер сразу на перечисленные методы — удобно для эндпоинтов, которые читают, но ничего не меняют.

Теперь про ошибки. Несуществующий путь — 404. Существующий путь с чужим методом — 405 Method Not Allowed: Fiber сам различает эти ситуации. Обе реакции переопределяются в ErrorHandler — единый формат ошибок сделаем в четырнадцатом уроке.

### Все методы сразу и несколько методов

```go
app.All("/health", func(c fiber.Ctx) error {
        return c.SendString("OK")
})

app.Add([]string{fiber.MethodGet, fiber.MethodHead}, "/ping",
        func(c fiber.Ctx) error {
                return c.SendString("pong")
        })
```

app.All применяет хендлер к любому методу по точному пути /health — healthcheck-эндпоинты так и пишутся. app.Add принимает слайс методов: fiber.MethodGet и fiber.MethodHead — константы из пакета fiber, писать "GET" строкой тоже можно, но константы не дают опечататься. Полный список методов, который понимает Fiber: Get, Head, Post, Put, Patch, Delete, Connect, Options, Trace.

### Доработайте маршруты

Вставьте пропущенные фрагменты: жадный остаток пути и опциональный параметр.

```go
// GET /assets/css/main.css -> "css/main.css"
app.Get("/assets/___", func(c fiber.Ctx) error {
        return c.SendString(c.Params("*"))
})

// Срабатывает и на /hello, и на /hello/bob
app.Get("/hello/:name___", func(c fiber.Ctx) error {
        return c.SendString("Привет, " + c.Params("name", "гость"))
})
```

Подсказка: Звезда — жадный остаток пути; вопросительный знак делает сегмент опциональным.

Правильные ответы в пропусках: `*`, `?`

### Словарь урока

- **:id** — Именованный параметр маршрута: матчит один сегмент, значение через c.Params("id").
- **:name?** — Опциональный параметр: маршрут срабатывает и без сегмента; Params вернёт дефолт.
- ***** — Жадный опциональный остаток пути любой вложенности; достаётся через c.Params("*").
- **+** — Жадный обязательный остаток пути: пустой хвост не матчится.
- **app.All** — Регистрирует хендлер на всех HTTP-методах по точному пути (Use работает по префиксу).
- **Порядок маршрутов** — Fiber берёт первый сматчившийся: точные пути объявляйте раньше параметризованных, wildcard — последним.

### Честно про запуск и что дальше

Код этого урока — готовый main.go: соберите его в одном файле и запустите локально через go run ., проверяя маршруты curl-ом; в браузерной песочнице сайта сетевые приложения не работают. Проверьте особенно порядок: перенесите /shop/hello ниже параметризованного маршрута и убедитесь, что результат изменился.

Дальше по плану — параметры запроса: c.Params лишь одна из точек, откуда приходят данные. Query-строка вида ?page=2&sort=desc разбирается методом c.Query с дефолтами и типовыми хелперами: [Урок 4: параметры и query-параметры](/courses/fiber/lessons/04-parametry-zaprosa/).

## Коротко: что запомнить

- Маршрут в Fiber — вызов app.Get/app.Post/app.Put/app.Delete с путём и хендлером.
- :id объявляет параметр пути; значение достаётся c.Params("id") и всегда приходит строкой.
- :name? делает сегмент опциональным, у Params есть дефолт вторым аргументом.
- * — жадный опциональный остаток пути, + — жадный обязательный; оба читаются через c.Params.
- Первый сматчившийся маршрут выигрывает: точные пути регистрируйте раньше параметров и wildcard.
- Несуществующий путь — 404, существующий путь с чужим методом — 405; оба переопределяются в ErrorHandler.

## Домашнее задание

Спроектируйте маршруты каталога книг: список, карточка по идентификатору, комментарии книги (/books/:id/comments), жанр с необязательным сегментом (/books/genre/:genre?) и раздача обложек (/covers/*). Зарегистрируйте их в правильном порядке и письменно объясните, почему порядок именно такой — проверьте себя переносом строк.

## Вопросы и ответы по уроку

### Чем :id отличается от * в маршруте Fiber?

:id матчит ровно один сегмент пути, звезда * забирает весь остаток любой вложенности и читается через c.Params("*"). Для одного сегмента звезда избыточна.

### Почему маршрут срабатывает и на /users, и на /users/?

По умолчанию хвостовой слэш не различается (StrictRouting выключен). Включите fiber.Config{StrictRouting: true}, если /users и /users/ должны быть разными маршрутами.

### Что вернёт DELETE на GET-маршрут?

405 Method Not Allowed, если путь существует для другого метода; 404 — если пути нет вовсе. Оба ответа настраиваются в ErrorHandler приложения.
