Rust 编写的简单实用的 Web 后端框架

Salvo 构建于 tokio 和 hyper 之上. 仅仅需要 Rust 基础知识即可开发功能强大的后端服务.
我们的目标是: 编码最简单, 功能不缺失, 性能有保障.

use salvo::prelude::*;
#[handler]
async fn hello_world(res: &mut Response) {
    res.render("Hello world!");
}
#[tokio::main]
async fn main() {
    let router = Router::new().get(hello_world);
    let listener = TcpListener::bind("127.0.0.1:7878");
    Server::new(listener).serve(router).await;
}

http://localhost:7878

Hello world!

链式树状路由系统

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

let debug_mode = true;
let admin_mode = true;
let router = Router::new()
    .get(handle)
    .push(
        Router::with_path("users")
            .hoop(auth)
            .post(handle)
            .push(Router::with_path("< id>").post(handle).delete(handle)),
    )
    .push(
        Router::with_path("users")
            .get(handle)
            .push(Router::with_path("< id>").get(handle)),
    )
    .then(|router| {
        if debug_mode {
            router.push(Router::with_path("debug").get(handle))
        } else {
            router
        }
    })
    .then(|router| {
        if admin_mode {
            parent.push(Router::with_path("admin").get(handle))
        } else {
            router
        }
    });

静态文件托管

非常简单地实现本地静态文件托管, 同时提供对 rust_embed 的支持.

#[derive(RustEmbed)]
#[folder = "test/static"]
struct Assets;

let router = Router::new()
    .push(Router::with_path("dir1/<*path>").get(StaticDir::new("static/body")))
    .push(Router::with_path("test1.txt").get(
        StaticFile::new("test/static/test1.txt").chunk_size(1024))
    )
    .push(Router::with_path("test2.txt").get(
        Assets::get("test1.txt").unwrap().into_handler())
    )
    .push(Router::with_path("dir2/<**path>").get(
        static_embed::<Assets>().with_fallback("index.html"))
    );

中间件系统

可以从预置的中间件中选择, 也可以创建自己的中间件. 中间件可以被添加到路由中, 比如: 验证用户登录状态, 压缩输出结果等.

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

支持 WebSocket

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

use futures::{FutureExt, StreamExt};
use salvo::prelude::*;
use salvo::extra::ws::WsHandler;
#[handler]
async fn connect(req: &mut Request, res: &mut Response) -> Result<(), StatusError> {
    WebSocketUpgrade::new()
        .upgrade(req, res, |mut ws| async move {
            while let Some(msg) = ws.recv().await {
                let msg = if let Ok(msg) = msg {
                    msg
                } else {
                    return;
                };

                if ws.send(msg).await.is_err() {
                    return;
                }
            }
        })
        .await
}
#[tokio::main]
async fn main() {
    let router = Router::new().handle(connect);
    Server::new(TcpListener::bind("127.0.0.1:7878")).serve(router).await;
}