salvo-middleware
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSalvo 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 to control execution flow:
FlowCtrlrust
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));
}中间件使用来控制执行流程:
FlowCtrlrust
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 to attach middleware:
hoop()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 checkMiddleware 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- : Call the next middleware or handler
call_next() - : Skip remaining middleware and handlers
skip_rest() - : Check if execution has been stopped
is_ceased()
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
常见内置中间件
| Middleware | Feature | Description |
|---|---|---|
| | Request/response logging |
| | Response compression (gzip, brotli) |
| | Cross-Origin Resource Sharing |
| | Request timeout handling |
| | CSRF protection |
| | Rate limiting |
| | Concurrent request limiting |
| | Request body size limiting |
| 中间件 | 特性 | 描述 |
|---|---|---|
| | 请求/响应日志记录 |
| | 响应压缩(gzip、brotli) |
| | 跨域资源共享 |
| | 请求超时处理 |
| | CSRF防护 |
| | 限流 |
| | 并发请求限制 |
| | 请求体大小限制 |
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
最佳实践
- Use to continue execution
ctrl.call_next() - Use to stop early
ctrl.skip_rest() - Store shared data in
Depot - Apply middleware at appropriate router level
- Order middleware by dependency (auth before authorization)
- Use built-in middleware when available
- Keep middleware focused on single concern
- Put logging middleware first to capture all requests
- 使用继续执行
ctrl.call_next() - 使用提前停止
ctrl.skip_rest() - 在中存储共享数据
Depot - 在合适的路由级别应用中间件
- 按依赖关系排序中间件(身份验证在授权之前)
- 优先使用内置中间件
- 保持中间件专注于单一关注点
- 将日志中间件放在首位,以捕获所有请求