一个基于 Rust 开发的简单易用的 Web 后端框架!

Salvo 是构建于 tokio 和 hyper 之上.

use salvo::prelude::*;
#[fn_handler]
async fn hello_world(res: &mut Response) {
    res.render_plain_text("Hello World!");
}
#[tokio::main]
async fn main() {
    let router = Router::new().get(hello_world);
    Server::new(router).bind(([0, 0, 0, 0], 7878)).await;
}

http://localhost:7878

Hello World!

树状路由系统

树状路由系统允许你非常方便地写出链式表达式. 通过筛选器可以设置路由的具体匹配规则. 在路径选择器中支持正则表达式约束.

let debug_mode = true;
let admin_mode = true;
let router = Router::new().get(handle).push(
    Router::new().path("users").before(auth).post(handle).push(
        Router::new().path("< id:/\d+/>").post(handle).delete(handle)
    ),
).push(Router::new().path("users").get(handle).push(
        Router::new().path("< id:/\d+/>").get(handle)
    ),
).push_when(|_|if debug_mode {
        Some(Router::new().path("debug").get(handle))
    } else {
        None
}).visit(|parent|{
    if admin_mode {
        parent.push(Router::new().path("admin").get(handle))
    } else {
        parent
    }
});

静态文件托管

可以定义静态文件或者文件夹句柄托管静态文件, 比如: HTML, CSS, and JavaScript. 支持将多个物理目录映射为同一个路径提供服务.

let router = Router::new()
    .path("files/<*path>")
    .get(StaticDir::new(vec!["static/body", "static/girl"]));
        

中间件系统

可以从预置的中间件中选择, 也可以创建自己的中间件. 中间件可以被添加到路由中, 在请求到达 Handler 之前或者之后作一些处理工作, 比如: 验证用户登录状态, 压缩输出结果等.

let router = Router::new().push(
    Router::new().path("ws_chat")
        .get(StaticFile::new("examples/ws_chat.rs"))
).push(
    Router::new().after(deflate()).path("sse_chat")
        .get(StaticFile::new("sse_chat.rs"))
).push(
    Router::new().after(brotli()).path("todos")
        .get(StaticFile::new("todos.rs"))
).push(
    Router::new().after(gzip()).path("<*path>")
        .get(StaticDir::new("examples/"))
);

支持 WebSocket

Salvo 支持 WebSocket! 使用 Salvo 可以方便地处理 WebSocket 请求.

use futures::{FutureExt, StreamExt};
use tokio;
use salvo::prelude::*;
use salvo_extra::ws::WsHandler;
#[fn_handler]
async fn connect(req: &mut Request, res: &mut Response) -> Result<(), HttpError> {
    let fut = WsHandler::new().handle(req, res)?;
    let fut = async move {
        if let Some(ws) = fut.await {
            let (tx, rx) = ws.split();
            let fut = rx.forward(tx).map(|result| {
                if let Err(e) = result {
                    eprintln!("websocket error: {:?}", e);
                }
            });
            tokio::task::spawn(fut);
        }
    };
    tokio::task::spawn(fut);
    Ok(())
}
#[tokio::main]
async fn main() {
    let router = Router::new().handle(connect);
    Server::new(router).bind(([0, 0, 0, 0], 7878)).await;
}