salvo-middleware

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Salvo Middleware

Salvo 中间件

This skill helps implement middleware in Salvo applications. In Salvo, middleware is just a handler with flow control - the same concept applies to both.
本技能可帮助你在Salvo应用中实现中间件。在Salvo中,中间件就是带有流程控制的处理器,二者概念一致。

Basic Middleware Pattern

基础中间件模式

Middleware uses
FlowCtrl
to control execution flow:
rust
use salvo::prelude::*;

#[handler]
async fn logger(req: &mut Request, depot: &mut Depot, res: &mut Response, ctrl: &mut FlowCtrl) {
    println!("Request: {} {}", req.method(), req.uri().path());

    // Continue to next handler
    ctrl.call_next(req, depot, res).await;

    println!("Response status: {}", res.status_code().unwrap_or(StatusCode::OK));
}
中间件使用
FlowCtrl
来控制执行流程:
rust
use salvo::prelude::*;

#[handler]
async fn logger(req: &mut Request, depot: &mut Depot, res: &mut Response, ctrl: &mut FlowCtrl) {
    println!("Request: {} {}", req.method(), req.uri().path());

    // Continue to next handler
    ctrl.call_next(req, depot, res).await;

    println!("Response status: {}", res.status_code().unwrap_or(StatusCode::OK));
}

Middleware Attachment

中间件挂载

Use
hoop()
to attach middleware:
rust
let router = Router::new()
    .hoop(logger)
    .hoop(auth_check)
    .get(handler);
Middleware applies to all child routes:
rust
let router = Router::new()
    .push(
        Router::with_path("api")
            .hoop(auth_check)  // Only applies to /api routes
            .get(protected_handler)
    )
    .get(public_handler);  // No auth check
使用
hoop()
方法挂载中间件:
rust
let router = Router::new()
    .hoop(logger)
    .hoop(auth_check)
    .get(handler);
中间件会应用于所有子路由:
rust
let router = Router::new()
    .push(
        Router::with_path("api")
            .hoop(auth_check)  // Only applies to /api routes
            .get(protected_handler)
    )
    .get(public_handler);  // No auth check

Middleware Scopes

中间件作用域

Global Middleware

全局中间件

rust
let router = Router::new()
    .hoop(global_middleware)  // Applies to all routes
    .push(Router::with_path("/api").get(api_handler))
    .push(Router::with_path("/admin").get(admin_handler));
rust
let router = Router::new()
    .hoop(global_middleware)  // Applies to all routes
    .push(Router::with_path("/api").get(api_handler))
    .push(Router::with_path("/admin").get(admin_handler));

Route-Level Middleware

路由级中间件

rust
let router = Router::new()
    .push(
        Router::with_path("/api")
            .hoop(api_middleware)  // Only applies to /api
            .get(api_handler)
    )
    .push(Router::with_path("/admin").get(admin_handler));
rust
let router = Router::new()
    .push(
        Router::with_path("/api")
            .hoop(api_middleware)  // Only applies to /api
            .get(api_handler)
    )
    .push(Router::with_path("/admin").get(admin_handler));

Combined Usage

组合使用

rust
let router = Router::new()
    .hoop(logger)  // Global logging
    .push(
        Router::with_path("/api")
            .hoop(auth_middleware)  // API authentication
            .hoop(rate_limiter)     // API rate limiting
            .get(api_handler)
    )
    .push(
        Router::with_path("/public")
            .get(public_handler)  // No auth required
    );
rust
let router = Router::new()
    .hoop(logger)  // Global logging
    .push(
        Router::with_path("/api")
            .hoop(auth_middleware)  // API authentication
            .hoop(rate_limiter)     // API rate limiting
            .get(api_handler)
    )
    .push(
        Router::with_path("/public")
            .get(public_handler)  // No auth required
    );

Common Middleware Patterns

常见中间件模式

Authentication

身份验证

rust
#[handler]
async fn auth_check(req: &mut Request, depot: &mut Depot, res: &mut Response, ctrl: &mut FlowCtrl) {
    let token = req.header::<String>("Authorization");

    match token {
        Some(token) if validate_token(&token) => {
            depot.insert("user_id", extract_user_id(&token));
            ctrl.call_next(req, depot, res).await;
        }
        _ => {
            res.status_code(StatusCode::UNAUTHORIZED);
            res.render("Unauthorized");
            ctrl.skip_rest();
        }
    }
}
rust
#[handler]
async fn auth_check(req: &mut Request, depot: &mut Depot, res: &mut Response, ctrl: &mut FlowCtrl) {
    let token = req.header::<String>("Authorization");

    match token {
        Some(token) if validate_token(&token) => {
            depot.insert("user_id", extract_user_id(&token));
            ctrl.call_next(req, depot, res).await;
        }
        _ => {
            res.status_code(StatusCode::UNAUTHORIZED);
            res.render("Unauthorized");
            ctrl.skip_rest();
        }
    }
}

Request Logging with Timing

带计时的请求日志

rust
#[handler]
async fn request_logger(req: &mut Request, depot: &mut Depot, res: &mut Response, ctrl: &mut FlowCtrl) {
    let start = std::time::Instant::now();
    let method = req.method().clone();
    let path = req.uri().path().to_string();

    ctrl.call_next(req, depot, res).await;

    let duration = start.elapsed();
    let status = res.status_code().unwrap_or(StatusCode::OK);
    println!("{} {} - {} ({:?})", method, path, status, duration);
}
rust
#[handler]
async fn request_logger(req: &mut Request, depot: &mut Depot, res: &mut Response, ctrl: &mut FlowCtrl) {
    let start = std::time::Instant::now();
    let method = req.method().clone();
    let path = req.uri().path().to_string();

    ctrl.call_next(req, depot, res).await;

    let duration = start.elapsed();
    let status = res.status_code().unwrap_or(StatusCode::OK);
    println!("{} {} - {} ({:?})", method, path, status, duration);
}

Add Custom Response Header

添加自定义响应头

rust
#[handler]
async fn add_custom_header(req: &mut Request, depot: &mut Depot, res: &mut Response, ctrl: &mut FlowCtrl) {
    res.headers_mut().insert("X-Custom-Header", "Salvo".parse().unwrap());
    ctrl.call_next(req, depot, res).await;
}
rust
#[handler]
async fn add_custom_header(req: &mut Request, depot: &mut Depot, res: &mut Response, ctrl: &mut FlowCtrl) {
    res.headers_mut().insert("X-Custom-Header", "Salvo".parse().unwrap());
    ctrl.call_next(req, depot, res).await;
}

CORS

CORS

rust
use salvo::cors::Cors;
use salvo::http::Method;

let cors = Cors::new()
    .allow_origin("https://example.com")
    .allow_methods(vec![Method::GET, Method::POST, Method::PUT, Method::DELETE])
    .allow_headers(vec!["Content-Type", "Authorization"])
    .into_handler();

let router = Router::new().hoop(cors);
rust
use salvo::cors::Cors;
use salvo::http::Method;

let cors = Cors::new()
    .allow_origin("https://example.com")
    .allow_methods(vec![Method::GET, Method::POST, Method::PUT, Method::DELETE])
    .allow_headers(vec!["Content-Type", "Authorization"])
    .into_handler();

let router = Router::new().hoop(cors);

Rate Limiting

限流

rust
use salvo::rate_limiter::{RateLimiter, FixedGuard, RemoteIpIssuer, BasicQuota, MokaStore};
use std::time::Duration;

let limiter = RateLimiter::new(
    FixedGuard::new(),
    MokaStore::new(),
    RemoteIpIssuer,
    BasicQuota::per_second(10),
);

let router = Router::new().hoop(limiter);
rust
use salvo::rate_limiter::{RateLimiter, FixedGuard, RemoteIpIssuer, BasicQuota, MokaStore};
use std::time::Duration;

let limiter = RateLimiter::new(
    FixedGuard::new(),
    MokaStore::new(),
    RemoteIpIssuer,
    BasicQuota::per_second(10),
);

let router = Router::new().hoop(limiter);

Using Depot for Data Sharing

使用Depot共享数据

Store data in middleware for use in handlers:
rust
#[handler]
async fn auth_middleware(req: &mut Request, depot: &mut Depot, res: &mut Response, ctrl: &mut FlowCtrl) {
    let user = authenticate(req).await;
    depot.insert("user", user);
    ctrl.call_next(req, depot, res).await;
}

#[handler]
async fn protected_handler(depot: &mut Depot) -> String {
    let user = depot.get::<User>("user").unwrap();
    format!("Hello, {}", user.name)
}
在中间件中存储数据,供处理器使用:
rust
#[handler]
async fn auth_middleware(req: &mut Request, depot: &mut Depot, res: &mut Response, ctrl: &mut FlowCtrl) {
    let user = authenticate(req).await;
    depot.insert("user", user);
    ctrl.call_next(req, depot, res).await;
}

#[handler]
async fn protected_handler(depot: &mut Depot) -> String {
    let user = depot.get::<User>("user").unwrap();
    format!("Hello, {}", user.name)
}

Type-Safe Depot Usage

类型安全的Depot使用

rust
// Store different types
depot.insert("string_value", "hello");
depot.insert("int_value", 42);
depot.insert("bool_value", true);

// Safely retrieve values (type must match)
if let Some(str_val) = depot.get::<&str>("string_value") {
    println!("String value: {}", str_val);
}

if let Some(int_val) = depot.get::<i32>("int_value") {
    println!("Int value: {}", int_val);
}
rust
// Store different types
depot.insert("string_value", "hello");
depot.insert("int_value", 42);
depot.insert("bool_value", true);

// Safely retrieve values (type must match)
if let Some(str_val) = depot.get::<&str>("string_value") {
    println!("String value: {}", str_val);
}

if let Some(int_val) = depot.get::<i32>("int_value") {
    println!("Int value: {}", int_val);
}

Early Response

提前响应

Stop execution and return response immediately:
rust
#[handler]
async fn validate_input(req: &mut Request, depot: &mut Depot, res: &mut Response, ctrl: &mut FlowCtrl) {
    if !is_valid_request(req) {
        res.status_code(StatusCode::BAD_REQUEST);
        res.render("Invalid request");
        ctrl.skip_rest();  // Stop processing
        return;
    }
    ctrl.call_next(req, depot, res).await;
}
立即停止执行并返回响应:
rust
#[handler]
async fn validate_input(req: &mut Request, depot: &mut Depot, res: &mut Response, ctrl: &mut FlowCtrl) {
    if !is_valid_request(req) {
        res.status_code(StatusCode::BAD_REQUEST);
        res.render("Invalid request");
        ctrl.skip_rest();  // Stop processing
        return;
    }
    ctrl.call_next(req, depot, res).await;
}

FlowCtrl Methods

FlowCtrl方法

FlowCtrl
provides methods to control middleware chain execution:
  • call_next()
    : Call the next middleware or handler
  • skip_rest()
    : Skip remaining middleware and handlers
  • is_ceased()
    : Check if execution has been stopped
FlowCtrl
提供了控制中间件链执行的方法:
  • call_next()
    :调用下一个中间件或处理器
  • skip_rest()
    :跳过剩余的中间件和处理器
  • is_ceased()
    :检查执行是否已停止

Built-in Middleware

内置中间件

Salvo provides many built-in middleware:
rust
use salvo::compression::Compression;
use salvo::cors::Cors;
use salvo::logging::Logger;
use salvo::timeout::Timeout;
use std::time::Duration;

let router = Router::new()
    .hoop(Logger::new())
    .hoop(Compression::new())
    .hoop(Cors::permissive())
    .hoop(Timeout::new(Duration::from_secs(30)));
Salvo提供了许多内置中间件:
rust
use salvo::compression::Compression;
use salvo::cors::Cors;
use salvo::logging::Logger;
use salvo::timeout::Timeout;
use std::time::Duration;

let router = Router::new()
    .hoop(Logger::new())
    .hoop(Compression::new())
    .hoop(Cors::permissive())
    .hoop(Timeout::new(Duration::from_secs(30)));

Common Built-in Middleware

常见内置中间件

MiddlewareFeatureDescription
Logger
logging
Request/response logging
Compression
compression
Response compression (gzip, brotli)
Cors
cors
Cross-Origin Resource Sharing
Timeout
timeout
Request timeout handling
CsrfHandler
csrf
CSRF protection
RateLimiter
rate-limiter
Rate limiting
ConcurrencyLimiter
concurrency-limiter
Concurrent request limiting
SizeLimiter
size-limiter
Request body size limiting
中间件特性描述
Logger
logging
请求/响应日志记录
Compression
compression
响应压缩(gzip、brotli)
Cors
cors
跨域资源共享
Timeout
timeout
请求超时处理
CsrfHandler
csrf
CSRF防护
RateLimiter
rate-limiter
限流
ConcurrencyLimiter
concurrency-limiter
并发请求限制
SizeLimiter
size-limiter
请求体大小限制

Middleware Execution Order (Onion Model)

中间件执行顺序(洋葱模型)

Middleware executes in an onion-like pattern:
rust
Router::new()
    .hoop(middleware_a)  // Runs first (outer layer)
    .hoop(middleware_b)  // Runs second
    .hoop(middleware_c)  // Runs third (inner layer)
    .get(handler);       // Core handler

// Execution order:
// middleware_a (before) -> middleware_b (before) -> middleware_c (before)
// -> handler
// -> middleware_c (after) -> middleware_b (after) -> middleware_a (after)
中间件以类似洋葱的模式执行:
rust
Router::new()
    .hoop(middleware_a)  // Runs first (outer layer)
    .hoop(middleware_b)  // Runs second
    .hoop(middleware_c)  // Runs third (inner layer)
    .get(handler);       // Core handler

// Execution order:
// middleware_a (before) -> middleware_b (before) -> middleware_c (before)
// -> handler
// -> middleware_c (after) -> middleware_b (after) -> middleware_a (after)

Best Practices

最佳实践

  1. Use
    ctrl.call_next()
    to continue execution
  2. Use
    ctrl.skip_rest()
    to stop early
  3. Store shared data in
    Depot
  4. Apply middleware at appropriate router level
  5. Order middleware by dependency (auth before authorization)
  6. Use built-in middleware when available
  7. Keep middleware focused on single concern
  8. Put logging middleware first to capture all requests
  1. 使用
    ctrl.call_next()
    继续执行
  2. 使用
    ctrl.skip_rest()
    提前停止
  3. Depot
    中存储共享数据
  4. 在合适的路由级别应用中间件
  5. 按依赖关系排序中间件(身份验证在授权之前)
  6. 优先使用内置中间件
  7. 保持中间件专注于单一关注点
  8. 将日志中间件放在首位,以捕获所有请求