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

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

use salvo::prelude::*;
#[fn_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.unwrap();
}

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
        }
    });

静态文件托管

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

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

中间件系统

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

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(DirHandler::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 {
                    println!("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(TcpListener::bind("127.0.0.1:7878")).serve(router).await.unwrap();
}