# Глава 1.3. Роутинг
Маршрутизация (роутинг) — это сопоставление запроса по его HTTP-методу и пути с конкретным хендлером, который этот запрос обработает. Axum использует декларативный подход к маршрутизации: вы не пишете `if path == "/users"`, а описываете таблицу соответствий «путь + метод → функция». `Router` — обычный тип, поэтому маршруты можно собирать из нескольких функций или модулей.
## Маршруты
Базовый синтаксис выглядит так:
```rust
use axum::{routing::get, Router};
let app = Router::new()
.route("/", get(root))
.route("/users", get(list_users).post(create_user))
.route("/users/{id}", get(get_user).delete(delete_user));
```
Функция `route` принимает путь и структуру `MethodRouter` — таблицу соответствий между HTTP-методами и хендлерами. Например, `.route("/users", get(list_users).post(create_user))` означает:
```text
GET /users -> list_users
POST /users -> create_user
```
Модуль `axum::routing` предоставляет функции для каждого HTTP-метода: `get`, `post`, `put`, `patch`, `delete` и т.д. Отдельно объявлять `head` не нужно: `get` обслуживает и его. Для любого метода есть функция `any`, а для нескольких методов сразу — `on`.
Один путь нельзя добавить в роутер дважды:
```rust
Router::new()
.route("/users", get(list_users))
.route("/users", post(create_user)); // panic
```
## Параметры пути
Часть пути можно сделать параметром, указав его имя в фигурных скобках. Значение извлекается экстрактором `Path`:
```rust
use axum::extract::Path;
async fn get_user(Path(id): Path<u64>) -> String {
format!("user {id}")
}
let app = Router::new().route("/users/{id}", get(get_user));
```
Если параметров несколько, их извлекают кортежем в том порядке, в котором они идут в пути:
```rust
// маршрут /users/{user_id}/posts/{post_id}
async fn get_post(
Path((user_id, post_id)): Path<(u64, u64)>,
) {}
```
Весь оставшийся путь целиком захватывает параметр со звёздочкой:
```rust
.route("/assets/{*path}", get(serve_asset))
```
Например, для запроса `/assets/css/main.css` в `path` попадёт значение `css/main.css`.
Вся особенность такого параметра — в звёздочке перед именем: она означает, что параметр захватывает весь оставшийся путь вместе со слэшами. Само имя может быть любым: `path` здесь не ключевое слово, а просто название параметра, как `id` в примерах выше.
Подробнее про экстракторы поговорим в главе 1.5.
### Приоритет маршрутов
```rust
.route("/users/me", get(current_user))
.route("/users/{id}", get(get_user))
```
Запрос `GET /users/me` попадёт в `current_user`. Причём порядок объявления маршрутов не важен, поэтому `/users/{id}` и `/users/me` можно поменять местами без изменения поведения. При этом маршруты, совпадающие по структуре, запрещены, даже если у параметров разные имена: `/users/{id}` и `/users/{user_id}` в одном роутере вызовут панику при запуске.
## 404, 405 и fallback
Axum различает два случая:
- `404 Not Found` — такого пути нет.
- `405 Method Not Allowed` — путь существует, но для него не зарегистрирован данный HTTP-метод.
Например, если зарегистрирован только `GET /users`, то `POST /users` получит `405` и заголовок `Allow` со списком доступных методов, а `GET /unknown` получит `404`. Полный список HTTP-методов и кодов ответа можно посмотреть на [MDN](https://developer.mozilla.org/ru/docs/Web/HTTP).
Учтите, что `/users` и `/users/` — это разные пути. По сложившемуся соглашению путь со слэшем на конце обозначает каталог, а без слэша — отдельный ресурс, как файл в файловой системе. Поэтому, если зарегистрирован только `/users`, запрос к `/users/` получит `404`: автоматического перенаправления между ними Axum не делает.
Для API часто полезно настроить собственный `fallback` — хендлер, который вызывается, когда ни один маршрут не подошёл:
```rust
use axum::{http::StatusCode, response::IntoResponse, Json};
use serde_json::json;
async fn not_found() -> impl IntoResponse {
let body = Json(json!({ "error": "not found" }));
(StatusCode::NOT_FOUND, body)
}
let app = Router::new()
.route("/", get(root))
.fallback(not_found);
```
## Основные методы `Router`
Помимо `route`, у `Router` есть несколько методов, которые часто используются при построении приложения.
### `merge`
Метод `merge` объединяет два роутера, сохраняя их пути:
```rust
let users = Router::new().route("/users", get(list_users));
let posts = Router::new().route("/posts", get(list_posts));
let app = Router::new().merge(users).merge(posts);
```
Если в объединяемых роутерах окажутся одинаковые пути, при запуске произойдёт паника.
### `nest`
Метод `nest` монтирует роутер под определённым префиксом:
```rust
let users = Router::new()
.route("/", get(list_users))
.route("/{id}", get(get_user));
let app = Router::new()
.nest("/users", users);
```
В результате получатся маршруты:
```text
GET /users
GET /users/{id}
```
Вложенный роутер не знает о своём префиксе, поэтому его можно смонтировать и в другое место, например под `/api/users`. Благодаря `merge` и `nest` маршруты удобно раскладывать по модулям: каждый модуль описывает свой роутер, а в `main` они собираются вместе.
Теперь мы знаем, как запрос попадает в нужный хендлер. В следующей главе разберёмся, что хендлер может вернуть и как Axum превращает это в HTTP-ответ.
Наша группа в telegram. Задавайте вопросы и общайтесь.
Задонатить. Поддержите развитие площадки.