Cache
Fournit un middleware de mise en cache.
Le middleware Cache peut mettre en cache le StatusCode
, les Headers
et le Body
d'une Response
. Pour le contenu déjà mis en cache, lors du traitement d'une requête ultérieure, le middleware Cache enverra directement le contenu en mémoire cache au client.
Remarque : ce plugin ne met pas en cache les Response
dont le Body
est de type ResBody::Stream
. Si appliqué à ce type de Response
, Cache ne traitera pas ces requêtes et ne générera pas d'erreur.
Fonctionnalités principales
-
CacheIssuer
fournit une abstraction pour la génération des clés de cache. RequestIssuer
en est une implémentation qui permet de définir quelles parties de l'URL de la requête ainsi que la Method
seront utilisées pour générer la clé de cache. Vous pouvez également définir votre propre logique de génération de clés. Les clés de cache ne sont pas nécessairement des chaînes de caractères - tout type satisfaisant les contraintes Hash + Eq + Send + Sync + 'static
peut servir de clé.
-
CacheStore
fournit les opérations de stockage et de récupération des données. MokaStore
est une implémentation intégrée de cache mémoire basée sur moka
. Vous pouvez également définir votre propre implémentation.
-
Cache
est une structure implémentant Handler
, contenant également un champ skipper
permettant d'ignorer certaines requêtes qu'on ne souhaite pas mettre en cache. Par défaut, MethodSkipper
est utilisé pour ignorer toutes les requêtes autres que Method::GET
.
Exemple de code d'implémentation interne :
impl<S, I> Cache<S, I> {
pub fn new(store: S, issuer: I) -> Self {
let skipper = MethodSkipper::new().skip_all().skip_get(false);
Cache {
store,
issuer,
skipper: Box::new(skipper),
}
}
}
Migration rapide depuis d'autres frameworks
Si vous avez déjà utilisé des mécanismes de cache dans d'autres frameworks, les correspondances conceptuelles suivantes vous aideront à vous adapter plus rapidement à l'implémentation du cache dans Salvo :
Guide de migration depuis Rust
Guide de migration depuis d'autres langages
-
Depuis Go/Gin : Gin utilise un modèle de middleware, tout comme Salvo :
// Exemple de cache dans Gin
store := persist.NewMemoryStore(time.Second * 60)
router.Use(cache.CachePage(store, time.Second * 30))
// Implémentation équivalente dans Salvo
let store = MokaStore::new(100).with_ttl(Duration::from_secs(30));
router.hoop(Cache::new(store, RequestIssuer::new()))
-
Depuis Spring Boot : Le cache déclaratif de Spring Boot doit être converti en configuration explicite de middleware dans Salvo :
// Spring Boot
@Cacheable(value = "books", key = "#isbn")
public Book findBook(ISBN isbn) { ... }
// Implémentation équivalente dans Salvo - application du cache au niveau des routes
let custom_issuer = YourCustomIssuer::new(); // implémente l'interface CacheIssuer
Router::with_path("books").hoop(Cache::new(MokaStore::new(100), custom_issuer))
-
Depuis Express.js : Le middleware de cache d'Express est conceptuellement similaire à Salvo, mais la syntaxe diffère :
// Express.js
const apicache = require('apicache');
app.use(apicache.middleware('5 minutes'));
// Implémentation équivalente dans Salvo
let store = MokaStore::new(100).with_ttl(Duration::from_secs(300));
router.hoop(Cache::new(store, RequestIssuer::new()))
Lors de la migration depuis d'autres frameworks, notez ces concepts clés du cache dans Salvo :
- Génération des clés de cache - contrôlée via l'interface
CacheIssuer
- Stockage du cache - implémenté via l'interface
CacheStore
- Logique d'ignorance du cache - personnalisable via le mécanisme
skipper
Par défaut, Salvo ne met en cache que les requêtes GET, ce qui correspond au comportement par défaut de la plupart des frameworks.
Exemple de code
cache-simple/src/main.rs
use std::time::Duration;
use salvo::cache::{Cache, MokaStore, RequestIssuer};
use salvo::prelude::*;
use salvo::writing::Text;
use time::OffsetDateTime;
// Handler for serving the home page with HTML content
#[handler]
async fn home() -> Text<&'static str> {
Text::Html(HOME_HTML)
}
// Handler for short-lived cached response (5 seconds)
#[handler]
async fn short() -> String {
format!(
"Hello World, my birth time is {}",
OffsetDateTime::now_utc()
)
}
// Handler for long-lived cached response (1 minute)
#[handler]
async fn long() -> String {
format!(
"Hello World, my birth time is {}",
OffsetDateTime::now_utc()
)
}
#[tokio::main]
async fn main() {
// Initialize logging system
tracing_subscriber::fmt().init();
// Create cache middleware for short-lived responses (5 seconds TTL)
let short_cache = Cache::new(
MokaStore::builder()
.time_to_live(Duration::from_secs(5))
.build(),
RequestIssuer::default(),
);
// Create cache middleware for long-lived responses (60 seconds TTL)
let long_cache = Cache::new(
MokaStore::builder()
.time_to_live(Duration::from_secs(60))
.build(),
RequestIssuer::default(),
);
// Set up router with three endpoints:
// - / : Home page
// - /short : Response cached for 5 seconds
// - /long : Response cached for 1 minute
let router = Router::new()
.get(home)
.push(Router::with_path("short").hoop(short_cache).get(short))
.push(Router::with_path("long").hoop(long_cache).get(long));
// Bind server to port 5800 and start serving
let acceptor = TcpListener::new("0.0.0.0:5800").bind().await;
Server::new(acceptor).serve(router).await;
}
// HTML template for the home page with links to cached endpoints
static HOME_HTML: &str = r#"
<!DOCTYPE html>
<html>
<head>
<title>Cache Example</title>
</head>
<body>
<h2>Cache Example</h2>
<p>
This examples shows how to use cache middleware.
</p>
<p>
<a href="/short" target="_blank">Cache 5 seconds</a>
</p>
<p>
<a href="/long" target="_blank">Cache 1 minute</a>
</p>
</body>
</html>
"#;