HTTP клиент на Rust: делаем запросы правильно от А до Я

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Введение: Почему Rust — отличный выбор для HTTP-запросов

Rust — это язык системного программирования, который завоевал популярность благодаря своей безопасности, производительности и отсутствию сборщика мусора. Если вы разрабатываете сетевое приложение, веб-скрапер или микросервис, умение правильно выполнять HTTP-запросы на Rust — обязательный навык. В этой статье мы разберём, как создать полноценный HTTP клиент на Rust с использованием библиотеки reqwest, которая является стандартом де-факто для работы с HTTP в экосистеме Rust.

Мы пройдём путь от установки зависимостей до обработки ошибок и работы с JSON. К концу статьи вы сможете писать надёжные и быстрые HTTP-клиенты на Rust, которые не подведут в production.

Установка и настройка проекта

Первый шаг — создание нового проекта и добавление зависимости reqwest с поддержкой асинхронности и JSON.

cargo new rust_http_client
cd rust_http_client

Откройте Cargo.toml и добавьте:

[dependencies]
reqwest = { version = "0.12", features = ["json"] }
tokio = { version = "1", features = ["full"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"

Объясним каждую зависимость:

  • reqwest — основной HTTP клиент. Флаг json включает поддержку сериализации/десериализации.
  • tokio — асинхронный рантайм Rust. HTTP-запросы в reqwest по умолчанию асинхронные.
  • serde / serde_json — для работы с JSON (структуры, сериализация).

Теперь всё готово для написания кода.

Базовый GET-запрос: получаем данные с сервера

Начнём с простого GET-запроса. Создадим функцию, которая получает данные с публичного API и выводит их в консоль.

use reqwest::Error;

#[tokio::main]
async fn main() -> Result<(), Error> {
    let url = "https://jsonplaceholder.typicode.com/posts/1";
    let response = reqwest::get(url).await?;

    println!("Статус: {}", response.status());

    let body = response.text().await?;
    println!("Тело ответа:\n{}", body);

    Ok(())
}

Что здесь происходит:

  • Мы используем макрос #[tokio::main] для запуска асинхронной функции.
  • reqwest::get(url).await? выполняет GET-запрос. Знак ? пробрасывает ошибку, если запрос не удался.
  • response.status() возвращает HTTP-статус (например, 200 OK).
  • response.text().await? извлекает тело ответа как строку.

Запустите код командой cargo run. Вы увидите JSON-объект с данными поста.

Добавляем заголовки и параметры запроса

В реальных приложениях часто нужно передавать заголовки (например, API-ключ) или query-параметры. Вот как это сделать:

use reqwest::Client;
use std::collections::HashMap;

#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
    let client = Client::new();

    let mut params = HashMap::new();
    params.insert("userId", "1");

    let response = client
        .get("https://jsonplaceholder.typicode.com/posts")
        .header("Accept", "application/json")
        .query(&params)
        .send()
        .await?;

    let posts: serde_json::Value = response.json().await?;
    println!("Количество постов: {}", posts.as_array().unwrap().len());

    Ok(())
}

Ключевые моменты:

  • Мы создаём Client — это переиспользуемый HTTP-клиент с пулом соединений. Всегда используйте Client вместо прямых вызовов reqwest::get для production-кода.
  • Метод .header() добавляет заголовок.
  • .query() принимает словарь параметров и автоматически кодирует URL.
  • .json() сразу десериализует ответ в нужный тип (здесь — serde_json::Value).

POST-запрос: отправляем данные на сервер

POST-запросы используются для создания ресурсов. Рассмотрим отправку JSON-данных.

use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, Debug)]
struct Post {
    title: String,
    body: String,
    userId: u32,
}

#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
    let client = reqwest::Client::new();

    let new_post = Post {
        title: "Rust HTTP клиент".to_string(),
        body: "Изучаем отправку POST-запросов".to_string(),
        userId: 1,
    };

    let response = client
        .post("https://jsonplaceholder.typicode.com/posts")
        .json(&new_post)
        .send()
        .await?;

    let created_post: Post = response.json().await?;
    println!("Созданный пост: {:#?}", created_post);

    Ok(())
}

Обратите внимание:

  • Мы определяем структуру Post с атрибутами #[derive(Serialize, Deserialize)]. Это позволяет serde автоматически конвертировать её в JSON и обратно.
  • Метод .json() автоматически сериализует структуру и устанавливает заголовок Content-Type: application/json.
  • Ответ от сервера мы снова десериализуем в ту же структуру.

Отправка формы (application/x-www-form-urlencoded)

Иногда нужно отправить данные в формате формы. Для этого используйте .form():

let mut form_data = std::collections::HashMap::new();
form_data.insert("username", "rust_user");
form_data.insert("password", "secret123");

let response = client
    .post("https://example.com/login")
    .form(&form_data)
    .send()
    .await?;

Обработка ошибок и статус-кодов

Надёжный HTTP-клиент должен корректно обрабатывать ошибки сети, таймауты и некорректные статус-коды.

use reqwest::StatusCode;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>&                

Блоги

Рекомендаций по книгам