Один обработчик на каждый маршрут
Маршрутизация определяет, какой обработчик отвечает на какой запрос. Встроенный маршрутизатор Go — это http.ServeMux: вы регистрируете шаблоны, а он распределяет запросы.
Шаблоны могут включать метод и параметры пути в фигурных скобках: "GET /users/{id}" соответствует GET /users/7 и передаёт вам 7 через r.PathValue("id"). Фреймворки добавляют удобства поверх этого, но повседневные потребности покрываются сразу из коробки.
Маршруты и значения из пути
Два маршрута: список по адресу /users и один пользователь по адресу /users/{id}. Часть в фигурных скобках — это параметр пути, тот кусок URL, который является данными.
Маршрут с параметром пути
Один и тот же mux направляет /users одному обработчику, а /users/7 — другому, и r.PathValue("id") извлекает 7 без какой-либо нарезки строк.
Вывод: программа на Go и захваченный вывод
package main
import (
"fmt"
"io"
"net/http"
"net/http/httptest"
)
func main() {
mux := http.NewServeMux()
mux.HandleFunc("GET /users", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "все пользователи: Ana, Ben, Cleo")
})
mux.HandleFunc("GET /users/{id}", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "один пользователь, id =", r.PathValue("id"))
})
server := httptest.NewServer(mux)
defer server.Close()
for _, path := range []string{"/users", "/users/7"} {
resp, _ := http.Get(server.URL + path)
body, _ := io.ReadAll(resp.Body)
resp.Body.Close()
fmt.Print("GET ", path, " -> ", string(body))
}
}
Параметры запроса
Часть после ? содержит параметры запроса — настройки для запроса, например текст поиска или ограничение: /search?q=go&limit=2. Они берутся из r.URL.Query(), всегда в виде строк, причём Get возвращает "" для отсутствующих.
Правило простое: путь говорит, какой ресурс нужен, а строка запроса — как его отдать: фильтры, сортировка, постраничный вывод.
Чтение строки запроса
Обработчик читает q и limit из строки запроса. Во втором запросе limit пропущен, поэтому обработчик использует значение по умолчанию — отсутствующие параметры это нормально, предусматривайте их.
Вывод: программа на Go и захваченный вывод
package main
import (
"fmt"
"io"
"net/http"
"net/http/httptest"
)
func main() {
mux := http.NewServeMux()
mux.HandleFunc("GET /search", func(w http.ResponseWriter, r *http.Request) {
q := r.URL.Query().Get("q")
limit := r.URL.Query().Get("limit")
if limit == "" {
limit = "10"
}
fmt.Fprintln(w, "поиск по", q, "с лимитом", limit)
})
server := httptest.NewServer(mux)
defer server.Close()
for _, path := range []string{"/search?q=go&limit=2", "/search?q=json"} {
resp, _ := http.Get(server.URL + path)
body, _ := io.ReadAll(resp.Body)
resp.Body.Close()
fmt.Print("GET ", path, " -> ", string(body))
}
}