Rust reqwest и API Aruba AOS-CX


Клиент 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(&params)
        .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(&params)
        .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(&params)
        .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
Вход в полноэкранный режим Выход из полноэкранного режима

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