Клиент AOS-CX rust reqwest
В этих фрагментах кода приведен пример вызовов API к API Aruba AOS-CX с использованием Rust reqwests.
1. Зависимости
Сначала нам нужно добавить наши зависимости в проект.
Для этого обновите файл cargo.toml следующим образом:
[dependencies]
reqwest = { version = "0.11", features = ["json", "cookies"] }
tokio = { version = "1", features = ["full"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1.0"
2. Вход/выход
Любое взаимодействие с API AOS-CX требует входа в систему, а для поддержания порядка — выхода из системы.
API использует имя пользователя/пароль для аутентификации.
С помощью этого фрагмента мы:
- Отправляем вызов API Login
- Проверяем код состояния запроса
- Вывести ошибку на экран, если вход не удался.
- Отправить вызов выхода из системы
- Проверяем код состояния вызова выхода из системы и выводим на экран, если он не равен 200.
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let ip_add: &str = "your-ip-address";
let full_url = format!("https://{}/rest/latest/", ip_add);
let client = reqwest::Client::builder()
.cookie_store(true)
.danger_accept_invalid_certs(true)
.build()?;
let params = [("username", "your-username"), ("password", "your-password")];
let resp = client
.post(format!("{}login", full_url))
.form(¶ms)
.send()
.await?;
if resp.status() != 200 {
println!("Login unsuccessful, code is {:#?}", resp.status());
}
let logout = client.post(format!("{}logout", full_url)).send().await?;
if logout.status() != 200 {
println!("Logout unsuccessful, code is {:#?}", logout.status());
} else {
println!("Logout successful");
}
Ok(())
}
3. Получить некоторые данные
После того как у нас есть фреймворк входа/выхода, мы можем вставлять вызовы API для получения, отправки, POST, PUT и DELETE данных.
Вот пример, который отправляет вызов GET для запроса прошивки устройства.
Ответ представляет собой JSON, который мы можем десериализовать, используя предопределенную структуру для обработки пар ключ-значение ответа.
- Сначала мы определим структуру, это будет ключ ‘current_version’, а значению присвоим тип String.
- Затем мы используем клиент reqwests для определения URL для GET и того, как обрабатывать возвращаемый JSON.
- Обратите внимание, что имя структуры ссылается на
json
в ответе.
use serde::Deserialize;
#[derive(Deserialize)]
struct Image {
current_version: String,
}
let firmware = client
.get(format!("{}firmware", full_url))
.send()
.await?
.json::<Image>()
.await?;
println!("{:?}", firmware.current_version);
4. Динамический разбор JSON
Пример, приведенный в шаге 3, предоставляет отличный способ обработки возвращаемого JSON, но нам нужно знать, какие пары ключ-значение возвращаются из API, чтобы иметь возможность составить структуру.
При исследовании API часто приходится методом проб и ошибок находить нужные данные.
Как же мы можем отправить вызов GET для получения данных, не зная, что будет возвращено?
Для этого мы можем динамически передавать ответ JSON в хэш-карту:
- Сначала нам нужно ввести
std::collections::HashMap;
в область видимости. - Затем мы отправляем тот же вызов GET, что и во втором примере.
- Но для JSON-ответа мы ссылаемся на динамическую хэш-карту, а не на struct.
- Недостатком является то, что мы не можем напрямую вызывать определенные ключи для предоставления данных, пары ключ-значение теперь являются просто строками, но мы можем распечатать весь ответ.
use std::collections::HashMap;
let firmware = client
.get(format!("{}firmware", full_url))
.send()
.await?
.json::<HashMap<String, String>>()
.await?;
println!("{:#?}", firmware);
5. Собираем все вместе с помощью структуры
Вот полный код с использованием struct, я добавил дополнительный ключ:
use serde::Deserialize;
#[derive(Deserialize)]
struct Image {
current_version: String,
booted_image: String,
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let ip_add: &str = "your-ip-address";
let full_url = format!("https://{}/rest/latest/", ip_add);
let client = reqwest::Client::builder()
.cookie_store(true)
.danger_accept_invalid_certs(true)
.build()?;
let params = [("username", "your-username"), ("password", "your-password")];
let resp = client
.post(format!("{}login", full_url))
.form(¶ms)
.send()
.await?;
if resp.status() != 200 {
println!("Login unsuccessful, code is {:#?}", resp.status());
}
let firmware = client
.get(format!("{}firmware", full_url))
.send()
.await?
.json::<Image>()
.await?;
println!(
"Current firmware image: {}nBoot firmware image: {}",
firmware.current_version, firmware.booted_image
);
let logout = client.post(format!("{}logout", full_url)).send().await?;
if logout.status() != 200 {
println!("Logout unsuccessful, code is {:#?}", logout.status());
} else {
println!("Logout successful");
}
Ok(())
}
Вот пример вывода:
➜ test1 git:(master) ✗ cargo run
Finished dev [unoptimized + debuginfo] target(s) in 0.09s
Running `target/debug/test1`
Current firmware image: FL.10.09.1020
Boot firmware image: primary
Logout successful
6. Собираем все вместе с помощью динамической хэш-карты
use std::collections::HashMap;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let ip_add: &str = "your-ip-address";
let full_url = format!("https://{}/rest/latest/", ip_add);
let client = reqwest::Client::builder()
.cookie_store(true)
.danger_accept_invalid_certs(true)
.build()?;
let params = [("username", "your-username"), ("password", "your-password")];
let resp = client
.post(format!("{}login", full_url))
.form(¶ms)
.send()
.await?;
if resp.status() != 200 {
println!("Login unsuccessful, code is {:#?}", resp.status());
}
let firmware = client
.get(format!("{}firmware", full_url))
.send()
.await?
.json::<HashMap<String, String>>()
.await?;
println!("{:#?}", firmware);
let logout = client.post(format!("{}logout", full_url)).send().await?;
if logout.status() != 200 {
println!("Logout unsuccessful, code is {:#?}", logout.status());
} else {
println!("Logout successful");
}
Ok(())
}
Образец вывода:
➜ test1 git:(master) ✗ cargo run
Compiling test1 v0.1.0 (/Users/joeneville/code/test1)
Finished dev [unoptimized + debuginfo] target(s) in 1.56s
Running `target/debug/test1`
{
"current_version": "FL.10.09.1020",
"primary_version": "FL.10.09.1020",
"secondary_version": "FL.10.09.1010",
"default_image": "primary",
"booted_image": "primary",
}
Logout successful