REST с помощью Rust

Эта статья была первоначально опубликована здесь.

Вызов REST API с помощью rust может показаться сложной задачей из-за крутой и долгой кривой обучения rust как языка программирования.

Мы знаем, что обращение к REST API — это то, с чем мы сталкиваемся при создании почти любого другого приложения, которое приходит нам на ум.

Мы воспользуемся библиотекой reqwest для выполнения нашего запроса, которая, по сути, является реализацией более высокого уровня стандартного HTTP-клиента.

# Cargo.toml
reqwest = { version = "0.11", features = ["json"] }
tokio = { version = "1", features = ["full"] }
dotenv = "0.15.0" # optional
serde = {version = "1.0.144", features = ["derive"]}
Вход в полноэкранный режим Выход из полноэкранного режима

Для этого мы импортируем следующие библиотеки

Библиотека Назначение
reqwest для выполнения наших запросов
tokio для создания асинхронных запросов и других асинхронных вещей.
serde для десериализации json-ответа в rust struct

Выполнение основных GET-запросов

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let resp = reqwest::get("<url>").await?;
    let resp_json = resp.json::<HashMap<String, String>>().await?;

    println!("{:#?}", resp_json);

    Ok(());
}
Вход в полноэкранный режим Выйти из полноэкранного режима

Здесь мы делаем следующие ключевые вещи:

  • Мы добавляем атрибут tokio::main к нашей функции main, чтобы использовать await внутри нашей функции.
  • Мы изменяем тип возврата главной функции с unit type — () на Result<(), Box<dyn std::error::Error>>, чтобы перехватить ошибки запроса, если таковые возникнут.
  • Затем мы делаем запрос с помощью функции get и ожидаем его, а также используем оператор turbofish, чтобы получить только возвращаемый тип future.
    • Подробнее о том, почему мы используем этот оператор, здесь.
  • Затем мы десериализуем JSON-ответ в HashMap<String, String> для удобства и ожидаем его.

Добавление заголовков к нашему запросу

Чтобы добавить заголовки к нашему запросу, мы сначала создадим клиент запроса и используем его для добавления заголовков в наше приложение.

use reqwest::header::HeaderMap;

#[tokio::main]
async fn main() {
    ...
    let client = reqwest::Client::new();
    let mut headers = HeaderMap::new();
    headers.insert("content-type", "application/json".parse().unwrap());
Вход в полноэкранный режим Выход из полноэкранного режима
use reqwest::header::HeaderMap;

#[tokio::main]
async fn main() {
    ...
    let client = reqwest::Client::new();
    let mut headers = HeaderMap::new();
    headers.insert("content-type", "application/json".parse().unwrap());
    headers.insert("Authorization", format!("Bearer {}", API_TOKEN).parse().unwrap());

    let resp = client.get("<url>")
                                .headers(headers)
                                .send()
                                .await?;

    ...
}
Войти в полноэкранный режим Выйти из полноэкранного режима

Вот что мы сделали выше:

  • Мы создаем клиент запроса для отправки нашего запроса.
  • Мы создаем изменяемый экземпляр экземпляра HeaderMap, который похож на HashMap.
  • Мы вставляем в него наши заголовки в виде пар ключ-значение.
    • Мы используем .parse().unwrap() на &str для преобразования строкового типа в тип значения заголовка.
  • Затем мы добавляем наши заголовки в запрос клиента с помощью метода .headers().
  • Также, одно отличие от прямого использования метода get заключается в том, что мы должны вызвать метод send для нашего запроса, прежде чем ожидать его.

Отправка post-запроса с телом JSON

Мы отправляем post-запрос с помощью метода post на клиенте запроса или непосредственно из библиотеки и используем метод json для добавления тела к post-запросу.

Тело здесь — это просто HashMap в rust.

...
let mut body = HashMap::new();
body.insert("username", "myusername");
let resp = client.post("<url>")
    .json(&body)
    .send()
    .await?;
...
Вход в полноэкранный режим Выход из полноэкранного режима

Десериализация ответа JSON

Мы можем десериализовать JSON-ответ от API, используя метод json на отправленном запросе, и получить его в желаемую форму или тип, вызвав его общий вызов с этим типом.

Следует отметить, что тип должен реализовывать свойство Deserialize.

Первым делом мы создаем тип, в котором мы хотим получить наш JSON-ответ, и реализуем для него признак deserialize. Реализация признака самостоятельно утомительна и ненадежна, поэтому мы используем библиотеку serde, которую мы импортировали ранее, чтобы сделать это за нас.

use serde::Deserialize;

// deriving the Debug trait also to be able to print it
#[derive(Debug, Deserialize)]
struct APIResponse {
    message: String;
    error: String;
}
Вход в полноэкранный режим Выход из полноэкранного режима

Затем мы можем использовать вышеупомянутый struct для десериализации нашего ответа:

...
let resp_json = resp.json::<APIResponse>().await?;
...
Вход в полноэкранный режим Выйти из полноэкранного режима

Ссылки

  • Документация по Rust ASYNC,
  • YouTube Tutorial by Tom.

Оцените статью
devanswers.ru
Добавить комментарий