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

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

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

Начать курс

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

Rust — это современный системный язык программирования, который сочетает в себе производительность C++ и безопасность памяти. При разработке веб-приложений, микросервисов или инструментов командной строки часто возникает необходимость взаимодействовать с внешними API. В этой статье мы разберём, как создать HTTP-клиент на Rust, используя популярные крейты reqwest и ureq. Вы узнаете, как отправлять GET и POST запросы, обрабатывать ошибки и работать с JSON.

Почему Rust для HTTP-запросов?

Rust предлагает уникальные преимущества для работы с сетью:

  • Безопасность памяти — компилятор гарантирует отсутствие утечек и гонок данных.
  • Высокая производительность — скорость сравнима с C/C++.
  • Асинхронность — встроенная поддержка async/await для эффективной работы с I/O.
  • Экосистема крейтов — тысячи готовых библиотек, включая HTTP-клиенты.

Для работы с HTTP в Rust есть два основных подхода: синхронный (блокирующий) и асинхронный (неблокирующий). Мы рассмотрим оба.

Настройка проекта и выбор крейта

Создадим новый проект Rust:

cargo new rust-http-client
cd rust-http-client

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

[dependencies]
# Для асинхронных запросов
reqwest = { version = "0.12", features = ["json"] }
tokio = { version = "1", features = ["full"] }

# Для синхронных запросов (альтернатива)
ureq = "2.9"

Сравнение крейтов:

  • reqwest — самый популярный HTTP-клиент. Поддерживает асинхронность, JSON, cookies, прокси.
  • ureq — лёгкий синхронный клиент. Идеален для простых скриптов и CLI-инструментов.

1. GET-запрос с reqwest (асинхронный)

Начнём с асинхронного GET-запроса. Создадим файл src/main.rs:

use reqwest;
use std::error::Error;

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
    // Отправляем GET-запрос
    let response = reqwest::get("https://api.github.com/repos/rust-lang/rust")
        .await?
        .text()
        .await?;

    println!("Ответ от GitHub API:");
    println!("{}", &response[..500]); // Выводим первые 500 символов

    Ok(())
}

Объяснение:

  • #[tokio::main] — макрос для запуска асинхронного рантайма Tokio.
  • reqwest::get() — отправляет GET-запрос и возвращает Response.
  • .await? — ожидает завершения операции и обрабатывает ошибки.
  • .text() — извлекает тело ответа как строку.

Запустите проект:

cargo run

Вы увидите JSON-ответ от GitHub API.

2. POST-запрос с JSON (reqwest)

Теперь отправим POST-запрос с телом в формате JSON:

use reqwest;
use serde::{Deserialize, Serialize};
use std::error::Error;

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

#[derive(Deserialize, Debug)]
struct CreatedPost {
    id: u32,
    title: String,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
    // Создаём клиент с настройками
    let client = reqwest::Client::new();

    // Данные для отправки
    let new_post = Post {
        title: "HTTP клиент на Rust".to_string(),
        body: "Полное руководство по работе с HTTP в Rust".to_string(),
        user_id: 1,
    };

    // Отправляем POST-запрос
    let response = client
        .post("https://jsonplaceholder.typicode.com/posts")
        .json(&new_post)
        .send()
        .await?;

    // Проверяем статус
    if response.status().is_success() {
        let created_post: CreatedPost = response.json().await?;
        println!("Пост создан: {:?}", created_post);
    } else {
        eprintln!("Ошибка: {}", response.status());
    }

    Ok(())
}

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

  • reqwest::Client::new() — создаёт пул соединений для повторного использования.
  • .json(&data) — автоматически сериализует структуру в JSON.
  • .send() — выполняет запрос.
  • response.json::<T>() — десериализует ответ в структуру Rust.

Не забудьте добавить serde в Cargo.toml:

serde = { version = "1", features = ["derive"] }

3. Синхронный HTTP-клиент с ureq

Для простых сценариев, где асинхронность не нужна, используйте ureq. Пример GET-запроса:

use ureq;

fn main() -> Result<(), ureq::Error> {
    let response = ureq::get("https://api.github.com/repos/rust-lang/rust")
        .set("User-Agent", "rust-http-client")
        .call()?;

    let body = response.into_string()?;
    println!("Ответ: {}", &body[..300]);

    Ok(())
}

POST-запрос с JSON:

use ureq;

fn main() -> Result<(), ureq::Error> {
    let response = ureq::post("https://jsonplaceholder.typicode.com/posts")
        .set("Content-Type", "application/json")
        .send_json(ureq::json!({
            "title": "HTTP клиент на Rust",
            "body": "Синхронный подход с ureq",
            "userId": 1
        }))?;

    let body: serde_json::Value = response.into_json()?;
    println!("Созданный пост: {}", body);

    Ok(())
}

Преимущества ureq:

  • Минимальные зависимости (нет

Блоги

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