Namespaces in Actix-Web
Let’s look at namespaces, scope, and state:
use actix_web::{web, App, HttpServer, Responder};
// Define handler functions for each scope
async fn index() -> impl Responder {
"Hello from /app/index!"
}
async fn dashboard() -> impl Responder {
"Hello from /dashboard/home!"
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.service(
// First scope: handles requests with the /app prefix
web::scope("/app")
.route("/index", web::get().to(index)),
)
.service(
// Second scope: handles requests with the /dashboard prefix
web::scope("/dashboard")
.route("/home", web::get().to(dashboard)),
)
})
.bind(("127.0.0.1", 8080))?
.run()
.await
}
use actix_web::{get, web, App, HttpServer, Responder};
use std::sync::Mutex;
use std::collections::HashMap;
use tokio::time::{self, Duration};
// This struct represents the state
struct AppState {
exchange_rates: Mutex<HashMap<String, f64>>, // A cache of exchange rates
}
// Function to simulate fetching exchange rates from an external API
async fn fetch_exchange_rates() -> HashMap<String, f64> {
// Simulated data; in a real-world scenario, you'd make an external HTTP request here
let mut rates = HashMap::new();
rates.insert("USD_EUR".to_string(), 0.92);
rates.insert("USD_GBP".to_string(), 0.81);
rates
}
// Background task to update the exchange rates periodically
async fn update_exchange_rates(state: web::Data<AppState>) {
let mut interval = time::interval(Duration::from_secs(3600)); // Update every hour
loop {
interval.tick().await;
let new_rates = fetch_exchange_rates().await;
let mut rates = state.exchange_rates.lock().unwrap();
*rates = new_rates;
}
}
Here’s a real-world use case where application state is essential in an Actix Web application. Consider a scenario where you have a web service that provides exchange rates for different currencies. You might want to maintain a cache of these rates in the application state to avoid frequent external API calls.
Use Case: Currency Exchange Rate Service
Goal: Create a web service that provides currency exchange rates. The service will keep an up-to-date cache of exchange rates in the application state, refreshing it periodically.
Example Code:
use actix_web::{get, web, App, HttpServer, Responder};
use std::sync::Mutex;
use std::collections::HashMap;
use tokio::time::{self, Duration};
// This struct represents the state
struct AppState {
exchange_rates: Mutex<HashMap<String, f64>>, // A cache of exchange rates
}
// Function to simulate fetching exchange rates from an external API
async fn fetch_exchange_rates() -> HashMap<String, f64> {
// Simulated data; in a real-world scenario, you'd make an external HTTP request here
let mut rates = HashMap::new();
rates.insert("USD_EUR".to_string(), 0.92);
rates.insert("USD_GBP".to_string(), 0.81);
rates
}
// Background task to update the exchange rates periodically
async fn update_exchange_rates(state: web::Data<AppState>) {
let mut interval = time::interval(Duration::from_secs(3600)); // Update every hour
loop {
interval.tick().await;
let new_rates = fetch_exchange_rates().await;
let mut rates = state.exchange_rates.lock().unwrap();
*rates = new_rates;
}
}
#[get("/rate/{pair}")]
async fn get_exchange_rate(
data: web::Data<AppState>,
path: web::Path<String>
) -> impl Responder {
let pair = path.into_inner();
let rates = data.exchange_rates.lock().unwrap();
if let Some(rate) = rates.get(&pair) {
format!("The exchange rate for {} is {}", pair, rate)
} else {
format!("Exchange rate for {} not found", pair)
}
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
let state = web::Data::new(AppState {
exchange_rates: Mutex::new(fetch_exchange_rates().await),
});
// Spawn a background task to update exchange rates periodically
let state_clone = state.clone();
tokio::spawn(update_exchange_rates(state_clone));
HttpServer::new(move || {
App::new()
.app_data(state.clone()) // Share state across routes
.service(get_exchange_rate)
})
.bind(("127.0.0.1", 8080))?
.run()
.await
}
#[get("/rate/{pair}")]
async fn get_exchange_rate(
data: web::Data<AppState>,
path: web::Path<String>
) -> impl Responder {
let pair = path.into_inner();
let rates = data.exchange_rates.lock().unwrap();
if let Some(rate) = rates.get(&pair) {
format!("The exchange rate for {} is {}", pair, rate)
} else {
format!("Exchange rate for {} not found", pair)
}
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
let state = web::Data::new(AppState {
exchange_rates: Mutex::new(fetch_exchange_rates().await),
});
// Spawn a background task to update exchange rates periodically
let state_clone = state.clone();
tokio::spawn(update_exchange_rates(state_clone));
HttpServer::new(move || {
App::new()
.app_data(state.clone()) // Share state across routes
.service(get_exchange_rate)
})
.bind(("127.0.0.1", 8080))?
.run()
.await
}
Explanation:
- State Structure (
AppState
): This holds aMutex
-protectedHashMap
to store exchange rates. - Fetch Function (
fetch_exchange_rates
): Simulates an external API call to get exchange rates. - Background Task: Uses
tokio::spawn
to periodically refresh the exchange rates without blocking the main thread. - Route (
/rate/{pair}
): Users can query for specific exchange rates by providing the currency pair (e.g.,USD_EUR
,USD_GBP
).
Benefits:
- Efficiency: Reduces the need for frequent external API requests by maintaining a local cache.
- State Sharing: The application state (
exchange_rates
) is shared across all incoming requests, ensuring consistent data.
https://docs.rs/actix-web/latest/actix_web/web/struct.Data.html