Tower Middleware Compatibility

Salvo provides compatibility support for the Tower ecosystem through the tower-compat feature. For specific APIs, please refer to the documentation.

Core Concepts of Tower

Tower is a service abstraction library primarily based on two core traits:

Service Trait

pub trait Service<Request> {
    type Response;
    type Error;
    type Future: Future<Output = Result<Self::Response, Self::Error>>;

    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>>;
    fn call(&mut self, req: Request) -> Self::Future;
}

The Service trait is responsible for handling requests and returning responses, similar to Salvo's handlers.

Layer Trait

pub trait Layer<S> {
    type Service;
    fn layer(&self, inner: S) -> Self::Service;
}

The Layer trait is used to wrap services and add additional functionality, similar to Salvo's middleware.

Salvo Compatibility Support

Salvo provides two key traits to achieve compatibility with the Tower ecosystem:

  • TowerLayerCompat: Converts Tower's Layer into Salvo's Handler, allowing it to be used as a Hoop (middleware).
  • TowerServiceCompat: Converts Tower's Service into Salvo's Handler.

Usage Example:

// Use Tower's RateLimitLayer and convert it to a Salvo-compatible format via the compat() method
let limit = RateLimitLayer::new(5, Duration::from_secs(30)).compat();
let router = Router::new().hoop(limit).get(hello);

Example Code

main.rs
Cargo.toml
use salvo::prelude::*;
use tokio::time::Duration;
use tower::limit::RateLimitLayer;

#[handler]
async fn hello() -> &'static str {
    "Hello World"
}

#[tokio::main]
async fn main() {
    tracing_subscriber::fmt().init();

    let limit = RateLimitLayer::new(5, Duration::from_secs(30)).compat();
    let acceptor = TcpListener::new("0.0.0.0:8698").bind().await;
    let router = Router::new().hoop(limit).get(hello);
    Server::new(acceptor).serve(router).await;
}