# Глава 1.4. Хендлеры и ответы Хендлер в Axum — это обычная асинхронная функция: ```rust async fn hello() -> &'static str { "Hello, World!" } ``` Фреймворк принимает такую функцию, если выполняются два условия: все её аргументы можно извлечь из запроса, а возвращаемое значение можно превратить в HTTP-ответ. Первое обеспечивают трейты `FromRequest` и `FromRequestParts` (глава 1.5), а второе — трейт `IntoResponse`. ## Что можно вернуть из хендлера Трейт `IntoResponse` описывает, как значение превращается в HTTP-ответ. Он уже реализован для большинства типов, которые могут понадобиться: | Возвращаем | Статус | `content-type` | | ------------------------ | ------------------------ | ------------------------------- | | `&'static str`, `String` | 200 | `text/plain; charset=utf-8` | | `Json<T>` | 200 | `application/json` | | `Html<T>` | 200 | `text/html; charset=utf-8` | | `Vec<u8>`, `Bytes` | 200 | `application/octet-stream` | | `()` | 200 | тела нет | | `StatusCode` | переданный код | тела нет | | `Redirect` | 303, 307 или 308 | тела нет, есть заголовок `Location` | | `Response` | задаётся при создании | задаётся при создании | Заголовок `content-type` выставляется в зависимости от типа возвращаемого значения, поэтому отдельно указывать его обычно не нужно. Именно поэтому в главе 1.1 в ответе `curl` появились заголовки, которые мы нигде не писали. ## Статус и заголовки Если нужно задать статус или заголовки ответа, их возвращают вместе с телом в кортеже. Последний элемент кортежа — тело ответа, а все элементы перед ним — статус и заголовки: ```rust use axum::{ http::{header, StatusCode}, response::IntoResponse, Json, }; async fn create_user() -> (StatusCode, Json<UserResponse>) { (StatusCode::CREATED, Json(user)) } async fn delete_user() -> StatusCode { StatusCode::NO_CONTENT } async fn export() -> impl IntoResponse { ( [(header::CONTENT_TYPE, "text/csv")], "id,name\n1,Alice", ) } ``` Статус и заголовки необязательны, но порядок фиксированный: сначала статус, потом заголовки, последним тело. Заголовки из кортежа заменяют те, что тип тела выставил сам, поэтому в `export` вместо `text/plain` уйдёт `text/csv`. ## Трейт `IntoResponse` Иногда хендлер может вернуть несколько разных вариантов ответа. В таком случае удобно указать в качестве возвращаемого типа `impl IntoResponse`: ```rust async fn get_user() -> impl IntoResponse { if let Some(user) = find_user() { Json(user).into_response() } else { StatusCode::NOT_FOUND.into_response() } } ``` Здесь ветки возвращают значения разных типов, поэтому каждое приводится к общему типу `Response` через `.into_response()`. Этот вызов обязателен: `impl IntoResponse` означает один конкретный тип, и без приведения код бы не скомпилировался. ## Свой тип ответа Трейт `IntoResponse` можно реализовать для собственного типа. Для этого нужна одна функция, которая превращает значение в `Response`: ```rust struct Created<T>(T); impl<T: Serialize> IntoResponse for Created<T> { fn into_response(self) -> Response { (StatusCode::CREATED, Json(self.0)).into_response() } } async fn create_user() -> Created<UserResponse> { Created(user) } ``` Такой подход полезен, когда один и тот же формат ответа используется в нескольких хендлерах. Мы разобрались, что хендлер может вернуть. В следующей главе посмотрим на другую сторону: как хендлер получает данные из запроса.
Наша группа в telegram. Задавайте вопросы и общайтесь.
Задонатить. Поддержите развитие площадки.