Представим, что мы разрабатываем веб-сайт с обучающими курсами, который состоит из нескольких страниц: о компании и со списком курсов. Нам в этом случае нужно так настроить веб-приложение, чтобы оно отображало разные страницы. В этом поможет маршрутизация.
В этом уроке мы разберем понятие маршрутизации. В веб-разработке — это важная составляющая, потому что она определяет, как веб-приложение обрабатывает различные HTTP-запросы пользователей.
Что такое роутинг
В Go-приложениях вся бизнес-логика описывается в обработчиках. Каждый обработчик — это логика поведения веб-приложения на определенный HTTP-запрос от клиента. Но веб-приложению нужно определить, какой обработчик используется для определенного запроса.
Когда веб-приложение получает запрос от клиента, оно сопоставляет HTTP-запрос с существующими обработчиками. Процесс определения обработчика по запросу называется маршрутизацией или роутингом.
Чаще всего роутинг осуществляется двумя способами:
- По пути HTTP-запроса
- По HTTP-методу запроса
Разберем каждый подход подробнее.
Роутинг по пути HTTP-запроса
Роутинг по пути HTTP-запроса используется во всех веб-приложениях. Путь — это часть URL, которая идет после домена. Например, в URL https://example.com/about
путь — это /about
.
Правила сопоставления пути HTTP-запроса с обработчиком описываются в коде при инициализации веб-приложения:
package main
import (
"github.com/gofiber/fiber/v2"
"github.com/sirupsen/logrus"
)
func main() {
webApp := fiber.New()
webApp.Get("/path1", path1Handler)
webApp.Get("/path2", path2Handler)
...
logrus.Fatal(webApp.Listen(":80"))
}
Когда приходит новый запрос от клиента, веб-приложение ищет соответствующий обработчик по пути HTTP-запроса. В примере выше при запросе по пути /path1
будет вызван обработчик path1Handler
, а при запросе по пути /path2
будет вызван обработчик path2Handler
.
Мы видим, чтобы в Fiber настроить роутинг на GET HTTP-запрос, достаточно вызвать метод webApp.Get()
и передать путь HTTP-запроса с нужным обработчиком.
Рассмотрим маршрутизацию по пути HTTP-запроса на конкретном примере. Нам нужно реализовать веб-приложение с обучающими курсами. Оно должно уметь обрабатывать два типа запросов:
- Запрос по пути
/about
должен возвращать текст «The best school for Software Engineers» - Запрос по пути
/courses
должен возвращать список курсов. Мы будем возвращать список курсов в виде строки «Java, Go, Python»
package main
import (
"github.com/gofiber/fiber/v2"
"github.com/sirupsen/logrus"
)
func main() {
webApp := fiber.New()
// Настраиваем обработчик по пути "/about"
webApp.Get("/about", func(c *fiber.Ctx) error {
return c.SendString("The best school for Software Engineers")
})
// Настраиваем обработчик по пути "/courses"
webApp.Get("/courses", func(c *fiber.Ctx) error {
return c.SendString("Java, Go, Python")
})
logrus.Fatal(webApp.Listen(":80"))
}
Запускаем веб-приложение и переходим в браузере на страницу со списком курсов: http://localhost/courses. Видим, что в ответ пришло сообщение "Java, Go, Python".
Если мы перейдем на страницу с информацией о компании http://localhost/about — в ответ приходит строка "The best school for Software Engineers".
Веб-приложение понимает, какой обработчик необходимо вызвать по пути HTTP-запросов. Поэтому мы получаем в ответе сообщение, которое соответствует HTTP-запросу. Но что будет, если мы отправим HTTP-запрос по несуществующему пути?
Если отправить запрос по несуществующему пути, например, /about2, то ответ будет таким: «Cannot GET /about2» с HTTP статусом 404 Not Found. Так происходит, потому что мы не настроили обработчик для пути /about2, и веб-приложение не знает, какой обработчик ему вызвать. В таком случае возвращается стандартный ответ с HTTP-статусом 404 Not Found.
Мы разобрали, как настроить роутинг по HTTP-запросам. В нашем примере веб-приложение только возвращало запрашиваемые данные. В иных случаях нам часто требуется не только возвращать данные, но и изменять их. Для этого используется роутинг по методам HTTP-запросов.
Роутинг по HTTP-методу запроса
HTTP-метод — это метод, который указывает, какое действие нужно выполнить по HTTP-пути запроса. Мы рассмотрим два наиболее популярных HTTP-метода:
- GET — получение данных
- POST — создание или изменение данных
В Fiber роутинг по HTTP-методу запроса настраивается с помощью специальных функций:
webApp.Get()
— для роутинга GET-запросовwebApp.Post()
— для роутинга POST-запросов
Рассмотрим роутинг по HTTP-методам на конкретном примере. Предположим, что мы разрабатываем онлайн-счетчик, который умеет обрабатывать следующие HTTP-запросы:
- POST /counter, который увеличивает счетчик на единицу
- GET /counter, который возвращает текущее значение счетчика и не изменяет его состояние
В этих HTTP-запросах совпадает путь, но различаются методы. И именно по ним веб-приложение понимает, какой обработчик нужно вызывать.
Так это выглядит на Go с использованием фреймворка Fiber:
package main
import (
"github.com/gofiber/fiber/v2"
"github.com/sirupsen/logrus"
"strconv"
)
var counter int64 = 0
func main() {
webApp := fiber.New()
webApp.Get("/counter", func(c *fiber.Ctx) error {
return c.SendString(strconv.FormatInt(counter, 10))
})
webApp.Post("/counter", func(c *fiber.Ctx) error {
counter++
return c.SendStatus(fiber.StatusOK)
})
logrus.Fatal(webApp.Listen(":80"))
}
Запускаем веб-приложение, открываем браузер и переходим на страницу http://localhost/counter. В ответ получаем строку «0». Нам вернулся корректный ответ, потому что мы еще не увеличивали счетчик.
Счетчик увеличивается с помощью POST-запроса. Есть разные способы осуществить такой запрос, но проще всего использовать утилиту curl. Для этого открываем терминал и выполняем следующую команду:
curl -X POST -v 'http://localhost/counter'
В ответ получаем статус «HTTP/1.1 200 OK» — успешное выполнение запроса. Переходим на страницу http://localhost/counter в браузере и видим, что счетчик увеличился на единицу.
Мы можем бесконечно увеличивать счетчик, и в любой момент при переходе на страницу http://localhost/counter мы получим текущее значение счетчика.
Так мы реализовали веб-приложение, которое умеет обрабатывать HTTP-запросы с разными методами, но одинаковыми путями.
Во всех примерах мы описывали статичные пути, когда запрос точно совпадает с описанным путем обработчика. В некоторых приложениях такого подхода недостаточно из-за дополнительных требований. Разберем, как их можно реализовать.
Что такое динамический роутинг и как его реализовать
Представим, что нам необходимо усложнить веб-приложение онлайн-счетчика и добавить возможность работать со счетчиками различных событий. Нам нужно, чтобы у каждого типа события было несколько счетчиков.
Чтобы работать с различными счетчиками, добавим возможность указывать название события в пути запроса. Например, для события click
будет путь запроса /counter/click
, а для события view
— путь запроса /counter/view
. Событий может быть много, поэтому нам нужно описать их. Для этого используем динамический роутинг.
Динамический роутинг позволяет указывать часть пути HTTP-запроса, которая будет изменяться в зависимости от запроса. Например, в нашем случае мы можем указать, что часть пути запроса /counter
будет всегда одинаковой. При этом вторая часть пути запроса будет меняться в зависимости от события /counter/:event
:
package main
import (
"github.com/gofiber/fiber/v2"
"github.com/sirupsen/logrus"
"strconv"
)
var counters = make(map[string]int64)
const requestParamKeyEvent = "event"
func main() {
webApp := fiber.New()
webApp.Get("/counter/:event", func(c *fiber.Ctx) error {
event := c.Params(requestParamKeyEvent, "")
if event == "" {
return c.SendStatus(fiber.StatusUnprocessableEntity)
}
eventCounter, ok := counters[event]
if !ok {
return c.SendStatus(fiber.StatusNotFound)
}
return c.SendString(strconv.FormatInt(eventCounter, 10))
})
webApp.Post("/counter/:event", func(c *fiber.Ctx) error {
event := c.Params(requestParamKeyEvent, "")
if event == "" {
return c.SendStatus(fiber.StatusUnprocessableEntity)
}
counters[event] += 1
return c.SendStatus(fiber.StatusOK)
})
logrus.Fatal(webApp.Listen(":80"))
}
Запустим приложение и проверим его работу. Для начала увеличим счетчик нескольких событий, а затем проверим, что у них корректное состояние:
- Отправим POST-запрос на
http://localhost/counter/clicks
три раза - Откроем в браузере страницу
http://localhost/counter/clicks
и проверим, что счетчик событияclicks
равен трем - Отправим POST-запрос на
http://localhost/counter/views
два раза - Откроем в браузере страницу
http://localhost/counter/views
и проверим, что счетчик событияviews
равен двум
Мы увеличили счетчики двух событий: clicks и views. После этого мы получили и проверили значения обоих счетчиков. Благодаря динамическому роутингу мы работали с двумя событиями, при этом использовали одни и те же обработчики. При необходимости мы можем добавлять новые события и не менять код обработчиков.
Выводы
В этом уроке мы разобрали, что такое роутинг — это процесс сопоставления HTTP-запроса с обработчиком, который будет обрабатывать этот запрос. Еще его называют маршрутизацией. Роутинг осуществляется с помощью путей и методов запроса, а динамический роутинг позволяет обрабатывать запросы с разными путями, и при этом использовать один обработчик.
Дополнительные материалы
Остались вопросы? Задайте их в разделе «Обсуждение»
Вам ответят команда поддержки Хекслета или другие студенты
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.