Эта статья была первоначально опубликована здесь.
Вызов 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.