Вызов 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"]}
Выполнение базовых запросов 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
к нашей основной функции, чтобы использовать ожидание внутри нашей функции. - Мы меняем возвращаемый тип основной функции с единичного типа —
()
на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
для нашего запроса, прежде чем ждать его.
Отправка почтового запроса с телом JSON
Мы отправляем запрос на публикацию с помощью метода post
в клиенте запросов или непосредственно из библиотеки и используем метод json
для добавления тела в запрос на публикацию.
Кузов тут просто HashMap
в ржавчине.
...
let mut body = HashMap::new();
body.insert("username", "myusername");
let resp = client.post("<url>")
.json(&body)
.send()
.await?;
...
Десериализация ответа JSON
Мы можем десериализовать ответ JSON от API, используя метод json
в отправленном запросе, и преобразовать его в предпочтительную форму или тип, вызывая его в общем случае с этим типом.
Следует отметить, что тип должен реализовывать трейт Deserialize.
Прежде всего, мы создаем тип, в котором мы хотим получить наш ответ JSON, и реализуем для него трейт десериализации, реализовать трейт самостоятельно утомительно и ненадежно, поэтому мы используем библиотеку serde
, которую мы импортировали ранее, чтобы сделать это за нас.
use serde::Deserialize;
// deriving the Debug trait also to be able to print it #[derive(Debug, Deserialize)] struct APIResponse { message: String; error: String; }
Затем мы можем использовать приведенную выше структуру для десериализации нашего ответа как:
...
let resp_json = resp.json::<APIResponse>().await?;
...