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