Rust Web开发新选择:Salvo框架初尝试

1. Salvo 0.84 新特性概览

Salvo 0.84在之前版本基础上进行了显著优化,主要包括:

  • 性能大幅提升:请求处理效率比0.58版本提高约40%
  • 异步处理优化:改善了异步任务调度机制
  • 路由系统增强:支持更灵活的路由匹配规则
  • 提取器改善:提供了更简洁的数据验证方式
  • 依赖管理简化:减少了对部分第三方库的依赖

2. 环境配置与项目创建

创建新项目并配置依赖:

cargo new salvo-demo
cd salvo-demo

更新 Cargo.toml:

[dependencies]
salvo = "0.84"
tokio = { version = "1.37", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }
tracing = "0.1"
tracing-subscriber = "0.3"

3. 基础应用快速入门

Salvo 0.84提供了更加简洁的API,以下是一个基础示例:

use salvo::prelude::*;

#[handler]
async fn hello() -> &'static str {
    "Hello, Salvo 0.84!"
}

#[handler]
async fn api_status() -> Json<serde_json::Value> {
    Json(serde_json::json!({
        "status": "ok",
        "version": "0.84"
    }))
}

#[tokio::main]
async fn main() {
    // 初始化日志
    tracing_subscriber::fmt().init();
    
    // 创建路由
    let router = Router::new()
        .get(hello)
        .push(Router::with_path("api/status").get(api_status));
    
    // 启动服务器
    let acceptor = TcpListener::new("0.0.0.0:5800").bind().await;
    Server::new(acceptor).serve(router).await;
}

4. 增强的路由系统

Salvo 0.84的路由系统更加强劲和灵活:

use salvo::prelude::*;

#[handler]
async fn get_users() -> &'static str {
    "获取用户列表"
}

#[handler]
async fn create_user() -> &'static str {
    "创建用户"
}

#[handler]
async fn get_user_detail(req: &mut Request) -> String {
    let user_id = req.param::<String>("id").unwrap();
    format!("用户ID: {}", user_id)
}

#[handler]
async fn update_user(req: &mut Request) -> String {
    let user_id = req.param::<String>("id").unwrap();
    format!("更新用户: {}", user_id)
}

#[tokio::main]
async fn main() {
    let user_router = Router::with_path("users")
        .get(get_users)
        .post(create_user)
        .push(
            Router::with_path("<id>")
                .get(get_user_detail)
                .patch(update_user)
                .delete(|| async { "删除用户" })
        );

    let api_router = Router::with_path("api")
        .push(user_router);

    let acceptor = TcpListener::new("0.0.0.0:5800").bind().await;
    Server::new(acceptor).serve(api_router).await;
}

5. 高级数据提取与验证

0.84版本增强了数据提取功能,支持更复杂的验证逻辑:

use salvo::prelude::*;
use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize, Extractible)]
#[salvo(extract(default_source(from = "body", format = "json")))]
struct CreateUserRequest {
    #[salvo(validate(length(min = 1, max = 50)))]
    name: String,
    
    #[salvo(validate(email))]
    email: String,
    
    #[salvo(validate(range(min = 1, max = 120)))]
    age: u8,
}

#[derive(Debug, Serialize, Deserialize, Extractible)]
#[salvo(extract(default_source(from = "query")))]
struct Pagination {
    page: Option<u32>,
    size: Option<u32>,
}

#[handler]
async fn create_user(validated_json: Json<CreateUserRequest>) -> impl Writer {
    let user_data = validated_json.into_inner();
    tracing::info!("创建用户: {:?}", user_data);
    StatusCode::CREATED
}

#[handler]
async fn list_users(pagination: Pagination) -> Json<serde_json::Value> {
    let page = pagination.page.unwrap_or(1);
    let size = pagination.size.unwrap_or(20);
    
    Json(serde_json::json!({
        "page": page,
        "size": size,
        "data": []
    }))
}

6. 中间件与请求处理管道

Salvo 0.84的中间件系统更加模块化:

use salvo::prelude::*;
use std::time::Instant;
use salvo::http::HeaderMap;

#[handler]
async fn logging_middleware(req: &mut Request, depot: &mut Depot, res: &mut Response, ctrl: &mut FlowCtrl) {
    let start_time = Instant::now();
    let path = req.uri().path().to_string();
    let method = req.method().to_string();
    
    ctrl.call_next(req, depot, res).await;
    
    let duration = start_time.elapsed();
    let status_code = res.status_code.unwrap_or(StatusCode::OK).as_u16();
    
    tracing::info!("{} {} - {} - {}ms", method, path, status_code, duration.as_millis());
}

#[handler]
async fn auth_middleware(req: &mut Request, depot: &mut Depot, res: &mut Response, ctrl: &mut FlowCtrl) {
    if let Some(auth_header) = req.headers().get("Authorization") {
        if let Ok(auth_str) = auth_header.to_str() {
            if auth_str.starts_with("Bearer ") {
                // 简单的token验证逻辑
                let token = &auth_str[7..];
                if token == "valid_token" {
                    depot.insert("user_id", "user_123");
                    ctrl.call_next(req, depot, res).await;
                    return;
                }
            }
        }
    }
    
    res.status_code(StatusCode::UNAUTHORIZED);
    res.render(Json(serde_json::json!({
        "error": "需要认证"
    })));
    ctrl.skip_rest();
}

#[handler]
async fn rate_limit_middleware(req: &mut Request, depot: &mut Depot, res: &mut Response, ctrl: &mut FlowCtrl) {
    // 简单的限流逻辑示例
    depot.insert("request_time", std::time::SystemTime::now());
    ctrl.call_next(req, depot, res).await;
}

7. WebSocket实时通信

0.84版本增强了WebSocket支持:

use salvo::prelude::*;
use salvo::ws::{WebSocketUpgrade, Message};
use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize)]
struct WsMessage {
    topic: String,
    data: serde_json::Value,
}

#[handler]
async fn chat_ws(req: &mut Request, depot: &mut Depot, res: &mut Response) {
    WebSocketUpgrade::new()
        .upgrade(req, depot, res)
        .await
        .unwrap();
}

#[handler]
async fn handle_websocket(ws: salvo::ws::WebSocket) {
    let (tx, mut rx) = ws.split();
    
    // 处理接收到的消息
    while let Some(result) = rx.recv().await {
        match result {
            Ok(message) => {
                if let Message::Text(text) = message {
                    tracing::info!("收到WebSocket消息: {}", text);
                    
                    // 这里可以处理消息并回复
                    if let Ok(parsed_msg) = serde_json::from_str::<WsMessage>(&text) {
                        match parsed_msg.topic.as_str() {
                            "ping" => {
                                let response = WsMessage {
                                    topic: "pong".to_string(),
                                    data: serde_json::json!({"timestamp": chrono::Utc::now()}),
                                };
                                if let Ok(response_text) = serde_json::to_string(&response) {
                                    let _ = tx.send(Message::text(response_text)).await;
                                }
                            }
                            _ => {
                                tracing::warn!("未知的消息主题: {}", parsed_msg.topic);
                            }
                        }
                    }
                }
            }
            Err(e) => {
                tracing::error!("WebSocket错误: {}", e);
                break;
            }
        }
    }
    
    tracing::info!("WebSocket连接关闭");
}

8. OpenAPI与API文档

Salvo 0.84继续提供一流的OpenAPI支持:

use salvo::prelude::*;
use salvo::oapi::endpoint;
use salvo::oapi::ToSchema;
use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize, ToSchema)]
struct User {
    id: u64,
    #[schema(example = "张三")]
    name: String,
    #[schema(example = "email@example.com")]
    email: String,
}

#[derive(Debug, Serialize, Deserialize, ToSchema)]
struct ApiResponse<T> {
    code: u32,
    message: String,
    data: Option<T>,
}

#[endpoint(
    tags("用户"),
    responses(
        (status_code = 200, description = "成功获取用户列表", body = ApiResponse<Vec<User>>)
    )
)]
async fn get_users() -> Json<ApiResponse<Vec<User>>> {
    let users = vec![
        User { id: 1, name: "张三".to_string(), email: "zhangsan@example.com".to_string() },
        User { id: 2, name: "李四".to_string(), email: "lisi@example.com".to_string() },
    ];
    
    Json(ApiResponse {
        code: 200,
        message: "成功".to_string(),
        data: Some(users),
    })
}

#[tokio::main]
async fn main() {
    let router = Router::new()
        .push(
            Router::with_path("api")
                .push(Router::with_path("users").get(get_users))
        );

    let doc = salvo::oapi::OpenApi::new("My API", "1.0.0").merge_router(&router);

    let router = router
        .push(doc.into_router("/api-doc/openapi.json"))
        .push(salvo::oapi::SwaggerUi::new("/api-doc/openapi.json").into_router("swagger-ui"));

    let acceptor = TcpListener::new("0.0.0.0:5800").bind().await;
    Server::new(acceptor).serve(router).await;
}

9. 错误处理最佳实践

0.84版本改善了错误处理机制:

use salvo::prelude::*;
use thiserror::Error;

#[derive(Error, Debug)]
enum AppError {
    #[error("用户未找到")]
    UserNotFound,
    #[error("无权限访问")]
    PermissionDenied,
    #[error("数据库错误: {0}")]
    DatabaseError(String),
}

impl Writer for AppError {
    fn write(mut self, _req: &Request, _depot: &Depot, res: &mut Response) {
        match self {
            AppError::UserNotFound => {
                res.status_code(StatusCode::NOT_FOUND);
            }
            AppError::PermissionDenied => {
                res.status_code(StatusCode::FORBIDDEN);
            }
            AppError::DatabaseError(_) => {
                res.status_code(StatusCode::INTERNAL_SERVER_ERROR);
            }
        }
        
        res.render(Json(serde_json::json!({
            "error": self.to_string(),
            "timestamp": chrono::Utc::now().to_rfc3339()
        })));
    }
}

#[handler]
async fn get_user_profile(req: &mut Request) -> Result<Json<serde_json::Value>, AppError> {
    let user_id = req.param::<u64>("id")
        .ok_or(AppError::UserNotFound)?;
    
    if user_id > 1000 {
        return Err(AppError::PermissionDenied);
    }
    
    // 模拟数据库操作
    if user_id == 0 {
        return Err(AppError::DatabaseError("连接失败".to_string()));
    }
    
    Ok(Json(serde_json::json!({
        "id": user_id,
        "name": "示例用户",
        "email": "user@example.com"
    })))
}

10. 生产环境部署配置

针对生产环境的完整配置示例:

use salvo::prelude::*;
use std::time::Duration;

#[handler]
async fn health_check() -> Json<serde_json::Value> {
    Json(serde_json::json!({
        "status": "healthy",
        "timestamp": chrono::Utc::now().to_rfc3339(),
        "version": env!("CARGO_PKG_VERSION")
    }))
}

#[handler]
async fn metrics_endpoint() -> &'static str {
    // 返回Prometheus格式的指标
    "# 应用指标示例
requests_total 42"
}

#[tokio::main]
async fn main() {
    // 生产环境日志配置
    tracing_subscriber::fmt()
        .json() // JSON格式便于日志收集
        .with_max_level(tracing::Level::INFO)
        .init();

    // 健康检查路由
    let health_router = Router::with_path("health")
        .get(health_check);
    
    // 指标路由
    let metrics_router = Router::with_path("metrics")
        .get(metrics_endpoint);
    
    // 业务API路由
    let api_router = Router::with_path("api")
        .hoop(logging_middleware)
        .hoop(rate_limit_middleware)
        .push(health_router)
        .push(metrics_router);
    
    // 静态文件服务
    let static_router = Router::with_path("static/<**>")
        .get(salvo::fs::StaticDir::new("static/"));
    
    let router = Router::new()
        .push(api_router)
        .push(static_router);
    
    // 生产环境配置
    let config = salvo::listener::TcpListener::new("0.0.0.0:8080")
        .bind()
        .await;
    
    tracing::info!("服务器启动在 http://0.0.0.0:8080");
    
    Server::new(config)
        .serve(router)
        .await;
}

总结

Salvo 0.84通过以下改善进一步巩固了其作为现代Rust Web框架的首选地位:

  1. 开发效率显著提升:更简洁的API设计和更好的错误信息
  2. 性能优化:异步任务调度和内存管理的改善
  3. 类型安全增强:利用Rust的类型系统提供更好的编译时保障
  4. 工具链完善:更好的调试和监控支持
© 版权声明

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
none
暂无评论...