مقدمة: لماذا كتابة عميل HTTP بلغة Rust؟
نادراً ما تخلو التطويرات الحديثة من التفاعل مع الخدمات الخارجية. سواء كانت واجهة برمجة تطبيقات REST للطقس، أو قاعدة بيانات، أو بنية الخدمات المصغرة - فإن القدرة على إرسال طلبات HTTP هي مهارة أساسية. تقدم Rust، مع تركيزها على الأداء والأمان، عدة حلول أنيقة للعمل مع HTTP. في هذه المقالة، سنشرح كيفية إنشاء عملاء HTTP بشكل صحيح بلغة Rust: من اختيار المكتبة إلى معالجة السيناريوهات المعقدة.
سننظر في أشهر مكتبتين: reqwest (المعيار غير المتزامن) و ureq (النهج المتزامن البسيط). ستتعلم كيفية إرسال طلبات GET و POST، ومعالجة JSON، وإدارة الرؤوس، وتجنب الأخطاء الشائعة.
1. اختيار المكتبة: reqwest مقابل ureq
قبل كتابة الكود، يجب تحديد الأداة. في نظام Rust البيئي، هناك مرشحان رئيسيان.
1.1. Reqwest - قوي وغير متزامن
reqwest هو المعيار الفعلي لطلبات HTTP في Rust. مبني على hyper و tokio، ويدعم عدم التزامن، والتسلسل/إلغاء التسلسل التلقائي لـ JSON، والعمل مع ملفات تعريف الارتباط. مثالي للتطبيقات عالية التحميل وخوادم الويب.
للبدء، أضف إلى Cargo.toml:
[dependencies]reqwest = { version = "0.12", features = ["json"] }tokio = { version = "1", features = ["full"] }serde = { version = "1.0", features = ["derive"] }serde_json = "1.0"1.2. Ureq - البساطة بدون عدم التزامن
إذا كنت لا تحتاج إلى استدعاءات غير متزامنة وتكتب نصاً برمجياً بسيطاً أو أداة CLI، فإن ureq سيكون خياراً ممتازاً. إنه متزامن، وبسيط، ولا يتطلب إعداداً معقداً لبيئة التشغيل.
[dependencies]ureq = "2.9"serde = { version = "1.0", features = ["derive"] }serde_json = "1.0"متى تختار ماذا؟ لخوادم الويب والتطبيقات المعقدة - reqwest. للمهام البسيطة والاختبارات والنصوص البرمجية - ureq.
2. الطلبات الأساسية: GET و POST
لننظر في أمثلة عملية لكلتا المكتبتين.
2.1. طلب GET باستخدام reqwest
أبسط مثال هو الحصول على بيانات من واجهة برمجة تطبيقات عامة. نستخدم الدالة غير المتزامنة main مع الماكرو #[tokio::main].
use reqwest;use std::error::Error;
#[tokio::main]async fn main() -> Result<(), Box<dyn Error>> { let url = "https://jsonplaceholder.typicode.com/posts/1"; let response = reqwest::get(url).await?;
// التحقق من حالة الاستجابة if response.status().is_success() { let body = response.text().await?; println!("الاستجابة: {}", body); } else { eprintln!("خطأ HTTP: {}", response.status()); }
Ok(())}2.2. طلب POST مع نص JSON (reqwest)
غالباً ما يتطلب إرسال البيانات تسلسل الهيكل. لهذا نستخدم crate serde.
use reqwest;use serde::{Deserialize, Serialize};use std::error::Error;
#[derive(Serialize, Deserialize, Debug)]struct Post { title: String, body: String, user_id: i32,}
#[tokio::main]async fn main() -> Result<(), Box<dyn Error>> { let client = reqwest::Client::new(); let new_post = Post { title: "Hello Rust".to_string(), body: "هذا هو نص الطلب".to_string(), user_id: 1, };
let response = client .post("https://jsonplaceholder.typicode.com/posts") .json(&new_post) .send() .await?;
let created_post: Post = response.json().await?; println!("المنشور الذي تم إنشاؤه: {:?}", created_post);
Ok(())}2.3. طلب GET بسيط باستخدام ureq
مع ureq يصبح الكود أقصر، حيث لا يتطلب عدم التزامن:
use ureq;use std::error::Error;
fn