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框架的首选地位:
- 开发效率显著提升:更简洁的API设计和更好的错误信息
- 性能优化:异步任务调度和内存管理的改善
- 类型安全增强:利用Rust的类型系统提供更好的编译时保障
- 工具链完善:更好的调试和监控支持
© 版权声明
文章版权归作者所有,未经允许请勿转载。
相关文章
暂无评论...