偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

你應(yīng)該知曉的 Rust Web 框架

開發(fā) 前端
正如我們所見,Rust Web? 框架的世界非常多樣化。沒有一種解決方案適用于所有情況,我們需要選擇最符合我們需求的框架。如果我們剛剛開始,我建議我們選擇 Actix? 或 Axum,因?yàn)樗鼈兪亲钸m合初學(xué)者的框架,而且它們有著出色的文檔。

前言

在之前的用 Rust 搭建 React Server Components 的 Web 服務(wù)器我們利用了Axum構(gòu)建了RSC的服務(wù)器。也算是用Rust在構(gòu)建Web服務(wù)上的小試牛刀。

雖然說Axum在Rust Web應(yīng)用中一枝獨(dú)秀。但是,市面上也有很多不同的解決方案。所以,今天我們就比較一些 Rust 框架,突出它們各自的優(yōu)勢(shì)和缺點(diǎn),以幫助我們?yōu)轫?xiàng)目做出明智的決策。沒有對(duì)比就沒有選擇,我們只有在真正的了解各個(gè)框架的優(yōu)缺點(diǎn)和適應(yīng)場(chǎng)景,在以后的開發(fā)中才能有的放矢的放心選擇。

文本中,我們會(huì)介紹很多Rust框架。并且會(huì)按照如下的受歡迎程度的順序來講。

圖片圖片

好了,天不早了,干點(diǎn)正事哇。

我們能所學(xué)到的知識(shí)點(diǎn)

  1. Axum
  2. Actix Web
  3. Rocket
  4. Warp
  5. Tide
  6. Poem

1. Axum

Axum[1] 是 Rust 生態(tài)系統(tǒng)中具有特殊地位的 Web 應(yīng)用程序框架(從下載量就可見端倪)。它是 Tokio 項(xiàng)目[2]的一部分,Tokio 是使用 Rust 編寫「異步網(wǎng)絡(luò)應(yīng)用程序的運(yùn)行時(shí)」。Axum 不僅使用 Tokio 作為其異步運(yùn)行時(shí),還與 Tokio 生態(tài)系統(tǒng)的其他庫(kù)集成,利用 Hyper[3] 作為其 HTTP 服務(wù)器和 Tower[4] 作為中間件。通過這樣做,我們能夠重用 Tokio 生態(tài)系統(tǒng)中現(xiàn)有的庫(kù)和工具。

Axum 「不依賴于宏」,而是利用 Rust 的類型系統(tǒng)提供安全且人性化的 API。這是通過使用特性來定義框架的核心抽象實(shí)現(xiàn)的,例如 Handler 特性,用于「定義應(yīng)用程序的核心邏輯」。這種方法允許我們輕松地「從較小的組件中組合應(yīng)用程序」,這些組件可以在多個(gè)應(yīng)用程序中重用。

在 Axum 中,處理程序(handler)是一個(gè)「接受請(qǐng)求并返回響應(yīng)」的函數(shù)。這與其他后端框架類似,但使用 Axum 的 FromRequest 特性,我們可以指定從請(qǐng)求中提取的數(shù)據(jù)類型。返回類型需要實(shí)現(xiàn) IntoResponse 特性(trait),已經(jīng)有許多類型實(shí)現(xiàn)了這個(gè)特性,包括允許輕松更改響應(yīng)的狀態(tài)代碼的元組類型。

Rust 的類型系統(tǒng)、泛型,尤其是在traits中使用異步方法(或更具體地說是返回的 Future),當(dāng)不滿足trait限制時(shí),Rust 的錯(cuò)誤消息會(huì)很復(fù)雜。特別是當(dāng)嘗試匹配抽象trait限制時(shí),經(jīng)常會(huì)得到一堆難以解讀的文本。為此Axum 提供了一個(gè)帶有輔助宏的庫(kù),將錯(cuò)誤放到實(shí)際發(fā)生錯(cuò)誤的地方,使得更容易理解發(fā)生了什么錯(cuò)誤。

雖然Axum 做了很多正確的事情,可以很容易地啟動(dòng)執(zhí)行許多任務(wù)的應(yīng)用程序。但是,有一些事情需要特別注意。Axum版本仍然低于 1.0,也就意味著Axum 團(tuán)隊(duì)保留在版本之間「根本性地更改 API 的自由」,這可能導(dǎo)致我們的應(yīng)用程序出現(xiàn)嚴(yán)重問題。

Axum 示例

下面展示了一個(gè) WebSocket 處理程序,它會(huì)回顯收到的任何消息。

// #[tokio::main] 宏標(biāo)記了 `main` 函數(shù),表明這是一個(gè)異步的`Tokio`應(yīng)用程序。
#[tokio::main]
async fn main() {
    // 首先創(chuàng)建了一個(gè) `TcpListener` 監(jiān)聽器,綁定到地址 "127.0.0.1:3000" 上
    // 然后,通過 `await` 等待監(jiān)聽器綁定完成
    // 如果綁定失敗,會(huì)通過 `unwrap` 方法拋出錯(cuò)誤。
    let listener = tokio::net::TcpListener::bind("127.0.0.1:3000")
        .await
        .unwrap();
    println!("listening on {}", listener.local_addr().unwrap());
    // 使用 `axum::serve` 啟動(dòng) Axum 框架的服務(wù)器,
    //    監(jiān)聽前面創(chuàng)建的 `TcpListener`。
    // `app()` 函數(shù)返回的是一個(gè) `Router`
    //    它定義了一個(gè)簡(jiǎn)單的路由,將路徑 "/a" 映射到處理函數(shù) `a_handler`。
    axum::serve(listener, app()).await.unwrap();
}

// 返回一個(gè) `Router`,它只有一個(gè)路由規(guī)則,
//    將 "/a" 路徑映射到 `a_handler` 處理函數(shù)
fn app() -> Router {
    Router::new()
        .route("/a", get(a_handler))
}

// 一個(gè)WebSocket處理程序,它會(huì)回顯收到的任何消息。
// 定義為一個(gè)WebSocket處理程序,
//   它接收一個(gè) `WebSocketUpgrade` 參數(shù),表示W(wǎng)ebSocket升級(jí)。
async fn a_handler(ws: WebSocketUpgrade) -> Response {
    // 調(diào)用將WebSocket升級(jí)后的對(duì)象傳遞給 `a_handle_socket` 處理函數(shù)。
    ws.on_upgrade(a_handle_socket)
}

async fn a_handle_socket(mut socket: WebSocket) {
    // 使用 while let 循環(huán),持續(xù)從 WebSocket 連接中接收消息。 
    // socket.recv().await 通過異步的方式接收消息,返回一個(gè) Result,
    // 其中 Ok(msg) 表示成功接收到消息。
    while let Some(Ok(msg)) = socket.recv().await {
        //  使用 if let 匹配,判斷接收到的消息是否為文本消息。
        //  WebSocket消息可以是不同類型的,這里我們只處理文本消息。
        if let Message::Text(msg) = msg {
            // 構(gòu)造一個(gè)回顯消息,將客戶端發(fā)送的消息包含在回顯消息中。
            // 然后,使用 socket.send 方法將回顯消息發(fā)送回客戶端。
            // await 等待發(fā)送操作完成。
            if socket
                .send(Message::Text(format!("You said: {msg}")))
                .await
                // 檢查 send 操作是否返回錯(cuò)誤。
                // 如果發(fā)送消息出現(xiàn)錯(cuò)誤(例如,連接斷開),
                // 就通過 break 跳出循環(huán),結(jié)束處理函數(shù)。
                .is_err()
            {
                break;
            }
        }
    }
}

Axum 特點(diǎn)

  • 無(wú)宏 API。
  • 利用 Tokio、Tower 和 Hyper 構(gòu)建強(qiáng)大的生態(tài)系統(tǒng)。
  • 出色的開發(fā)體驗(yàn)。
  • 仍處于 0.x 版本,因此可能發(fā)生重大變更。

2. Actix Web

Actix Web[5] 是 Rust 中存在已久且非常受歡迎的 Web 框架之一。像任何良好的開源項(xiàng)目一樣,它經(jīng)歷了許多迭代,但已經(jīng)達(dá)到了主要版本(不再是 0.x),換句話說:在主要版本內(nèi),它可以確保沒有破壞性的更改。

乍一看,Actix Web 與 Rust 中的其他 Web 框架非常相似。我們使用宏來定義 HTTP 方法和路由(類似于 Rocket),并使用提取器(extractors)從請(qǐng)求中獲取數(shù)據(jù)(類似于 Axum)。與 Axum 相比,它們之間的相似之處顯著,甚至在它們命名概念和特性的方式上也很相似。最大的區(qū)別是 Actix Web 沒有將自己與Tokio 生態(tài)系統(tǒng)強(qiáng)關(guān)聯(lián)在一起。雖然 Tokio 仍然是 Actix Web 底層的運(yùn)行時(shí),但是該框架具有自己的抽象和特性,以及自己的一套 crates 生態(tài)系統(tǒng)。這既有利有弊。一方面,我們可以確保事物能夠很好地配合使用,另一方面,我們可能會(huì)錯(cuò)失 Tokio 生態(tài)系統(tǒng)中已經(jīng)可用的許多功能。

Actix Web 實(shí)現(xiàn)了自己的 Service 特性,它基本上與 Tower 的 Service 相同,但仍然不兼容。這意味著在 Tower 生態(tài)系統(tǒng)中大多數(shù)可用的中間件在 Actix 中不可用。

如果在 Actix Web 中需要實(shí)現(xiàn)一些特殊任務(wù),而需要自己實(shí)現(xiàn),我們可能會(huì)碰到運(yùn)行框架中的 Actor 模型。這可能會(huì)增加一些意想不到的問題。

但 Actix Web 社區(qū)很給力。該框架支持 HTTP/2 和 WebSocket 升級(jí),提供了用于 Web 框架中最常見任務(wù)的 crates 和指南,以及出色的文檔,而且速度很快。Actix Web 之所以受歡迎,是有原因的,「如果我們需要保證版本,請(qǐng)注意它可能是我們目前的最佳選擇」。

Actix Web 示例

在 Actix Web 中,一個(gè)簡(jiǎn)單的 WebSocket 回顯服務(wù)器如下所示:

use actix::{Actor, StreamHandler};
use actix_web::{
  web, 
  App, 
  Error, 
  HttpRequest, 
  HttpResponse, 
  HttpServer
};
use actix_web_actors::ws;

/// 定義HTTP Actor
// 定義了一個(gè)名為 MyWs 的結(jié)構(gòu)體,這將用作WebSocket的Actix Actor。
// Actors 是Actix框架中的并發(fā)單元,用于處理異步消息
struct MyWs;

// 為 MyWs 結(jié)構(gòu)體實(shí)現(xiàn)了 Actor trait,指定了 WebsocketContext 作為上下文類型。
impl Actor for MyWs {
    type Context = ws::WebsocketContext<Self>;
}

/// 處理ws::Message消息的處理程序
// 為 MyWs 結(jié)構(gòu)體實(shí)現(xiàn)了 StreamHandler trait,處理WebSocket連接中的消息。
impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for MyWs {
    // 對(duì)接收到的不同類型的消息進(jìn)行處理。例如,對(duì)于 Ping 消息,發(fā)送 Pong 消息作為響應(yīng)。
    fn handle(&mut self, msg: Result<ws::Message, ws::ProtocolError>, ctx: &mut Self::Context) {
        match msg {
            Ok(ws::Message::Ping(msg)) => ctx.pong(&msg),
            Ok(ws::Message::Text(text)) => ctx.text(text),
            Ok(ws::Message::Binary(bin)) => ctx.binary(bin),
            _ => (),
        }
    }
}

// 定義了一個(gè)處理HTTP請(qǐng)求的異步函數(shù)。
async fn index(req: HttpRequest, stream: web::Payload) -> Result<HttpResponse, Error> {
    // 將WebSocket連接升級(jí),并將請(qǐng)求委托給 MyWs Actor 處理。
    let resp = ws::start(MyWs {}, &req, stream);
    println!("{:?}", resp);
    resp
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    //  創(chuàng)建了一個(gè) HttpServer 實(shí)例,通過 App::new() 創(chuàng)建一個(gè)應(yīng)用,
    // 該應(yīng)用只有一個(gè)路由,將路徑 "/ws/" 映射到處理函數(shù) index 上。
    HttpServer::new(|| App::new().route("/ws/", web::get().to(index)))
        // 綁定服務(wù)器到地址 "127.0.0.1" 和端口 8080。
        .bind(("127.0.0.1", 8080))?
        // 啟動(dòng)服務(wù)器并等待其完成運(yùn)行。
        .run()
        .await
}

Actix Web 特點(diǎn)

  • 擁有強(qiáng)大的生態(tài)系統(tǒng)。
  • 基于 Actor 模型。
  • 通過主要版本保證的穩(wěn)定 API。
  • 出色的文檔。

3. Rocket

Rocket[6] 在 Rust Web 框架生態(tài)系統(tǒng)中已經(jīng)有一段時(shí)間了:它的主要特點(diǎn)是基于宏的路由、內(nèi)置表單處理、對(duì)數(shù)據(jù)庫(kù)和狀態(tài)管理的支持,以及其自己版本的模板!Rocket 確實(shí)盡力做到構(gòu)建 一個(gè) Web 應(yīng)用程序所需的一切。

然而,Rocket 的雄心壯志也帶來了一些代價(jià)。盡管仍在積極開發(fā)中,但發(fā)布的頻率不如以前。這意味著框架的用戶會(huì)錯(cuò)過許多重要的東西。

此外,由于其一體化的方法,我們還需要了解 Rocket 的實(shí)現(xiàn)方式。Rocket 應(yīng)用程序有一個(gè)「生命周期」,構(gòu)建塊以特定的方式連接,如果出現(xiàn)問題,我們需要理解問題出在哪里。

Rocket 是一個(gè)很棒的框架,如果我們想開始使用 Rust 進(jìn)行 Web 開發(fā),它是一個(gè)很好的選擇。「對(duì)于我們?cè)S多人來說,Rocket 是進(jìn)入 Rust 的第一步」,使用它仍然很有趣。

Rocket 示例

處理表單的 Rocket 應(yīng)用程序的簡(jiǎn)化示例:

// 定義了一個(gè)名為 Password 的結(jié)構(gòu)體,該結(jié)構(gòu)體派生了 Debug 和 FromForm traits。
// FromForm trait 用于從表單數(shù)據(jù)中提取數(shù)據(jù)。
// 該結(jié)構(gòu)體包含兩個(gè)字段 first 和 second,分別表示密碼的第一個(gè)和第二個(gè)部分。
#[derive(Debug, FromForm)]
struct Password<'v> {
    // 表示對(duì)字段的長(zhǎng)度進(jìn)行了驗(yàn)證,要求長(zhǎng)度在6個(gè)字符以上
    #[field(validate = len(6..))]
    // 表示第一個(gè)字段必須等于第二個(gè)字段
    #[field(validate = eq(self.second))]
    first: &'v str,
    //  表示第二個(gè)字段必須等于第一個(gè)字段。
    #[field(validate = eq(self.first))]
    second: &'v str,
}

// 省略其他結(jié)構(gòu)體和實(shí)現(xiàn)...

// 定義了一個(gè)處理GET請(qǐng)求的函數(shù) index,返回一個(gè) Template 對(duì)象。
// 這個(gè)函數(shù)用于渲染首頁(yè)。
#[get("/")]
fn index() -> Template {
    Template::render("index", &Context::default())
}

// 定義了一個(gè)處理POST請(qǐng)求的函數(shù) submit。
// 這個(gè)函數(shù)接受一個(gè) Form 對(duì)象,其中包含了表單的數(shù)據(jù)
#[post("/", data = "<form>")]
fn submit(form: Form<Submit<'_>>) -> (Status, Template) {
    // 通過檢查 form.value 是否包含 Some(ref submission) 來判斷表單是否提交。
    let template = match form.value {
        // 如果提交了表單,打印提交的內(nèi)容,并渲染 "success" 頁(yè)面;
        Some(ref submission) => {
            println!("submission: {:#?}", submission);
            Template::render("success", &form.context)
        }
        // 否則,渲染 "index" 頁(yè)面。
        None => Template::render("index", &form.context),
    };

    (form.context.status(), template)
}

// 定義了啟動(dòng)Rocket應(yīng)用程序的函數(shù)。
#[launch]
fn rocket() -> _ {
    // 使用 rocket::build() 創(chuàng)建一個(gè)Rocket應(yīng)用程序?qū)嵗?    rocket::build()
        // 并通過 .mount() 方法掛載路由。
        // routes![index, submit] 定義了兩個(gè)路由,
        // 分別映射到 index 和 submit 函數(shù)。
        .mount("/", routes![index, submit])
        // 添加了一個(gè)模板處理的Fairing(Rocket中的中間件)
        .attach(Template::fairing())
        // 將靜態(tài)文件服務(wù)掛載到根路徑。
        .mount("/", FileServer::from(relative!("/static")))
}

Rocket 特點(diǎn)

  • 一體化的方法。
  • 出色的開發(fā)體驗(yàn)。
  • 開發(fā)活躍度不如以前。
  • 初學(xué)者的絕佳選擇。

4. Warp

Warp[7] 是一個(gè)構(gòu)建在 Tokio 之上的 Web 框架,而且是一個(gè)非常好的框架。它與我們之前看到的其他框架非常不同。

Warp 與 Axum 有一些共同的特點(diǎn):它構(gòu)建在 Tokio 和 Hyper 之上,并利用了 Tower 中間件。然而,它在方法上有很大的不同。Warp 是建立在 Filter trait 之上的。

在 Warp 中,我們構(gòu)建一系列應(yīng)用于傳入請(qǐng)求的過濾器,并將請(qǐng)求傳遞到管道直到達(dá)到末端。過濾器可以鏈接,它們可以組合。這使我們能夠構(gòu)建非常復(fù)雜的管道,但仍然易于理解。

Warp 也比 Axum 更接近 Tokio 生態(tài)系統(tǒng),這意味著我們可能會(huì)在沒有任何粘合特性的情況下處理更多 Tokio 結(jié)構(gòu)和概念。

Warp 采用非常功能化的方法,如果這是我們的編程風(fēng)格,我們將喜歡 Warp 的表達(dá)能力和可組合性。當(dāng)我們查看 Warp 代碼片段時(shí),它通常讀起來像正在發(fā)生的事情的故事,這在 Rust 中能夠?qū)崿F(xiàn)是有趣且令人驚訝的。

然而,隨著這些不同的函數(shù)和過濾器被鏈接在一起,Warp 中的類型變得非常長(zhǎng)且非常復(fù)雜,而且難以理解。錯(cuò)誤消息也是如此,可能是難以理解的一大堆文本。

Warp 是一個(gè)很棒的框架。但是,它「并不是最適合初學(xué)者的框架,也不是最流行的框架」。這意味著我們可能在尋找?guī)椭唾Y源方面會(huì)更加困難。但它非?!高m用于快速小型應(yīng)用程序」!

Warp 示例

來自其示例倉(cāng)庫(kù)的 WebSocket 聊天的 Warp 應(yīng)用程序的簡(jiǎn)化示例:

// 定義了一個(gè)靜態(tài)的原子 usize 計(jì)數(shù)器,用于為每個(gè)連接的用戶分配唯一的用戶ID。
static NEXT_USER_ID: AtomicUsize = AtomicUsize::new(1);

// 當(dāng)前連接用戶的狀態(tài)。
//  定義了一個(gè)類型別名 Users,它是一個(gè)原子引用計(jì)數(shù)的可讀寫鎖的 HashMap,將用戶ID映射到消息的發(fā)送器。
// Arc 是原子引用計(jì)數(shù)的智能指針,RwLock 是讀寫鎖。
// - 鍵是其id
// - 值是`warp::ws::Message`的發(fā)送器
type Users = Arc<RwLock<HashMap<usize, mpsc::UnboundedSender<Message>>>>;

#[tokio::main]
async fn main() {
    // 創(chuàng)建了一個(gè) users 變量,用于存儲(chǔ)連接的用戶信息
    let users = Users::default();
    // 將其包裝成 Warp 過濾器,以便在不同的路由中共享用戶狀態(tài)。
    let users = warp::any().map(move || users.clone());

    // chat 路由處理 WebSocket 握手
    let chat = warp::path("chat")
        // `ws()`過濾器將準(zhǔn)備WebSocket握手...
        .and(warp::ws())
        .and(users)
        // 調(diào)用 user_connected 函數(shù)處理 WebSocket 連接。
        .map(|ws: warp::ws::Ws, users| {
            // 如果握手成功,將調(diào)用我們的函數(shù)。
            ws.on_upgrade(move |socket| user_connected(socket, users))
        });

    // 處理 HTTP GET 請(qǐng)求,返回一個(gè)包含聊天室鏈接的 HTML 頁(yè)面
    let index = warp::path::end().map(|| warp::reply::html(INDEX_HTML));

    let routes = index.or(chat);

    warp::serve(routes).run(([127, 0, 0, 1], 3030)).await;
}


async fn user_connected(ws: WebSocket, users: Users) {
    // 使用計(jì)數(shù)器為此用戶分配新的唯一ID。
    let my_id = NEXT_USER_ID.fetch_add(1, Ordering::Relaxed);

    eprintln!("new chat user: {}", my_id);

    // 將套接字拆分為消息的發(fā)送器和接收器。
    let (mut user_ws_tx, mut user_ws_rx) = ws.split();
    // 創(chuàng)建一個(gè)新的消息通道 (mpsc::unbounded_channel) 用于將用戶的消息廣播給其他用戶
    let (tx, rx) = mpsc::unbounded_channel();
    let mut rx = UnboundedReceiverStream::new(rx);

    tokio::task::spawn(async move {
        // 不斷接收用戶的消息。一旦用戶斷開連接,就會(huì)退出這個(gè)循環(huán)。
        while let Some(message) = rx.next().await {
            user_ws_tx
                .send(message)
                .unwrap_or_else(|e| {
                    eprintln!("websocket send error: {}", e);
                })
                .await;
        }
    });

    //將發(fā)送器保存在我們的已連接用戶列表中。
    users.write().await.insert(my_id, tx);

    // 返回一個(gè)基本上是管理此特定用戶連接的狀態(tài)機(jī)的“Future”。

    // 每當(dāng)用戶發(fā)送消息時(shí),將其廣播給
    // 所有其他用戶...
    while let Some(result) = user_ws_rx.next().await {
        let msg = match result {
            Ok(msg) => msg,
            Err(e) => {
                eprintln!("websocket error(uid={}): {}", my_id, e);
                break;
            }
        };
        user_message(my_id, msg, &users).await;
    }

    // 只要用戶保持連接,user_ws_rx流就會(huì)繼續(xù)處理。一旦他們斷開連接,那么...
    user_disconnected(my_id, &users).await;
}

// 處理用戶發(fā)送的消息。它跳過非文本消息,將文本消息格式化為 <User#ID>: Message,然后將其廣播給所有其他用戶。
async fn user_message(my_id: usize, msg: Message, users: &Users) {
    // 跳過任何非文本消息...
    let msg = if let Ok(s) = msg.to_str() {
        s
    } else {
        return;
    };

    let new_msg = format!("<User#{}>: {}", my_id, msg);

    // 來自此用戶的新消息,將其發(fā)送給所有其他用戶(除了相同的uid)...
    for (&uid, tx) in users.read().await.iter() {
        if my_id != uid {
            if let Err(_disconnected) = tx.send(Message::text(new_msg.clone())) {
                // 發(fā)送器已斷開連接,我們的`user_disconnected`代碼
                // 應(yīng)該在另一個(gè)任務(wù)中執(zhí)行,這里沒有更多的事情要做。
            }
        }
    }
}

async fn user_disconnected(my_id: usize, users: &Users) {
    eprintln!("good bye user: {}", my_id);

    // 流關(guān)閉,因此從用戶列表中刪除
    users.write().await.remove(&my_id);
}

Warp 特點(diǎn)

  • 函數(shù)式方法。
  • 良好的表達(dá)能力。
  • 通過接近 Tokio、Tower 和 Hyper 構(gòu)建強(qiáng)大的生態(tài)系統(tǒng)。
  • 不適合初學(xué)者的框架

5. Tide

Tide[8] 是一個(gè)建立在 async-std 運(yùn)行時(shí)之上的「極簡(jiǎn)主義 Web 框架」。極簡(jiǎn)主義的方法意味著我們得到了一個(gè)非常小的 API 表面。Tide 中的處理函數(shù)是 async fn,接受一個(gè) Request 并返回一個(gè) Response 的 tide::Result。提取數(shù)據(jù)或發(fā)送正確的響應(yīng)格式由我們自行完成。

雖然這可能對(duì)我們來說是更多的工作,但也更直接,意味著我們完全掌控正在發(fā)生的事情。在某些情況下,能夠離 HTTP 請(qǐng)求和響應(yīng)如此近是一種愉悅,使事情變得更容易。

Tide 的中間件方法與我們從 Tower 中了解的類似,但 Tide 公開了 async trait crate,使實(shí)現(xiàn)變得更加容易。

Tide 示例

來自其示例倉(cāng)庫(kù)的用戶會(huì)話示例:

// async-std crate 提供的異步 main 函數(shù)。它返回一個(gè) Result,表示可能的錯(cuò)誤。
#[async_std::main]
async fn main() -> Result<(), std::io::Error> {
    // 使用 femme crate 啟用顏色日志。這是一個(gè)美觀的日志記錄庫(kù),可以使日志輸出更易讀。
    femme::start();
    // 創(chuàng)建一個(gè) Tide 應(yīng)用程序?qū)嵗?    let mut app = tide::new();
    // 添加一個(gè)日志中間件,用于記錄請(qǐng)求和響應(yīng)的日志信息。
    app.with(tide::log::LogMiddleware::new());
    // 添加一個(gè)會(huì)話中間件,用于處理會(huì)話數(shù)據(jù)。這里使用內(nèi)存存儲(chǔ),并提供一個(gè)密鑰(TIDE_SECRET),用于加密和驗(yàn)證會(huì)話數(shù)據(jù)。
    app.with(tide::sessions::SessionMiddleware::new(
        tide::sessions::MemoryStore::new(),
        std::env::var("TIDE_SECRET")
            .expect(
                "Please provide a TIDE_SECRET value of at \
                      least 32 bytes in order to run this example",
            )
            .as_bytes(),
    ));
    // 添加一個(gè) Before 中間件,它在處理請(qǐng)求之前執(zhí)行。在這里,它用于增加訪問計(jì)數(shù),存儲(chǔ)在會(huì)話中。
    app.with(tide::utils::Before(
        |mut request: tide::Request<()>| async move {
            let session = request.session_mut();
            let visits: usize = session.get("visits").unwrap_or_default();
            session.insert("visits", visits + 1).unwrap();
            request
        },
    ));
    // 定義了一個(gè)處理根路徑的GET請(qǐng)求的路由。這個(gè)路由通過 async move 來處理請(qǐng)求,獲取會(huì)話中的訪問計(jì)數(shù),并返回一個(gè)包含訪問次數(shù)的字符串。
    app.at("/").get(|req: tide::Request<()>| async move {
        let visits: usize = req.session().get("visits").unwrap();
        Ok(format!("you have visited this website {} times", visits))
    });
    // 定義了一個(gè)處理 "/reset" 路徑的GET請(qǐng)求的路由。這個(gè)路由通過 async move 處理請(qǐng)求,將會(huì)話數(shù)據(jù)清除,然后重定向到根路徑
    app.at("/reset")
        .get(|mut req: tide::Request<()>| async move {
            req.session_mut().destroy();
            Ok(tide::Redirect::new("/"))
        });
    // 啟動(dòng)應(yīng)用程序并監(jiān)聽在 "127.0.0.1:8080" 地址上。使用 await? 處理可能的啟動(dòng)錯(cuò)誤。
    app.listen("127.0.0.1:8080").await?;

    Ok(())
}

Tide 簡(jiǎn)要概述

  • 極簡(jiǎn)主義方法。
  • 使用 async-std 運(yùn)行時(shí)。
  • 簡(jiǎn)單的處理函數(shù)。
  • 異步特性的試驗(yàn)場(chǎng)。

6. Poem

Poem[9] 聲稱自己是一個(gè)功能齊全但易于使用的 Web 框架。乍一看,它的使用方式與 Axum 非常相似,唯一的區(qū)別是它需要使用相應(yīng)的宏標(biāo)記處理程序函數(shù)。它還建立在 Tokio 和 Hyper 之上,完全兼容 Tower 中間件,同時(shí)仍然暴露自己的中間件特性。

Poem 的中間件特性也非常簡(jiǎn)單易用。我們可以直接為所有或特定的 Endpoint(Poem 表達(dá)一切都可以處理 HTTP 請(qǐng)求的方式)實(shí)現(xiàn)該特性,或者只需編寫一個(gè)接受 Endpoint 作為參數(shù)的異步函數(shù)。

Poem 不僅與更廣泛的生態(tài)系統(tǒng)中的許多功能兼容,而且還具有豐富的功能,包括對(duì) OpenAPI 和 Swagger 文檔的全面支持。它不僅限于基于 HTTP 的 Web 服務(wù),還可以用于基于 Tonic 的 gRPC 服務(wù),甚至在 Lambda 函數(shù)中使用,而無(wú)需切換框架。添加對(duì) OpenTelemetry、Redis、Prometheus 等的支持,我們就可以勾選所有現(xiàn)代企業(yè)級(jí)應(yīng)用程序 Web 框架的所有框。

Poem 仍然處于 0.x 版本,但如果保持勢(shì)頭并交付出色的 1.0 版本,這將是一個(gè)值得關(guān)注的框架!

Poem 示例

來自其示例倉(cāng)庫(kù)的 WebSocket 聊天的縮寫版本:

// 注解表示這是一個(gè)處理器函數(shù),用于處理 WebSocket 請(qǐng)求
#[handler]
fn ws(
    // 提取了 WebSocket 路徑中的名字參數(shù)
    Path(name): Path<String>,
    // WebSocket 對(duì)象,表示與客戶端的連接
    ws: WebSocket,
    // 是一個(gè)數(shù)據(jù)提取器,用于獲取廣播通道的發(fā)送器。
    sender: Data<&tokio::sync::broadcast::Sender<String>>,
) -> impl IntoResponse {
    // 克隆了廣播通道的發(fā)送器 sender。
    let sender = sender.clone();
    // 它訂閱了廣播通道,創(chuàng)建了一個(gè)接收器 receiver
    let mut receiver = sender.subscribe();
    //  處理 WebSocket 連接升級(jí)
    ws.on_upgrade(move |socket| async move {
        // 將連接的讀寫部分拆分為 sink 和 stream
        let (mut sink, mut stream) = socket.split();
        
        // 從 WebSocket 客戶端接收消息
        // 如果是文本消息,則將其格式化為 {name}: {text} 的形式,并通過廣播通道發(fā)送。
        // 如果發(fā)送失敗(例如,通道關(guān)閉),則任務(wù)終止。

        tokio::spawn(async move {
            while let Some(Ok(msg)) = stream.next().await {
                if let Message::Text(text) = msg {
                    if sender.send(format!("{name}: {text}")).is_err() {
                        break;
                    }
                }
            }
        });
        // 從廣播通道接收消息,并將其發(fā)送到 WebSocket 客戶端
        tokio::spawn(async move {
            while let Ok(msg) = receiver.recv().await {
                if sink.send(Message::Text(msg)).await.is_err() {
                    break;
                }
            }
        });
    })
}

#[tokio::main]
async fn main() -> Result<(), std::io::Error> {
    // 使用 tide::Route 創(chuàng)建了一個(gè)路由,其中包括兩個(gè)路徑:
    // - / 路徑處理 HTTP GET 請(qǐng)求,調(diào)用 index 函數(shù)。
    // - /ws/:name 路徑處理 WebSocket 請(qǐng)求,調(diào)用 ws 函數(shù)。
    let app = Route::new().at("/", get(index)).at(
        "/ws/:name",
        // 通過 tokio::sync::broadcast::channel 創(chuàng)建一個(gè)廣播通道;
        // 并通過 tokio::sync::broadcast::channel::<String>(32).0 
        //   獲取其發(fā)送器,將其作為數(shù)據(jù)傳遞給 ws 處理函數(shù)
        get(ws.data(tokio::sync::broadcast::channel::<String>(32).0)),
    );
    // 創(chuàng)建了一個(gè)服務(wù)器實(shí)例
    Server::new(TcpListener::bind("127.0.0.1:3000"))
         // 啟動(dòng)服務(wù)器,并等待其完成運(yùn)行。
        .run(app)
        .await
}

Poem 簡(jiǎn)要概述

  • 豐富的功能集。
  • 與 Tokio 生態(tài)系統(tǒng)兼容。
  • 易于使用。
  • 適用于 gRPC 和 Lambda。

后記

正如我們所見,Rust Web 框架的世界非常多樣化。沒有一種解決方案適用于所有情況,我們需要選擇最符合我們需求的框架。如果我們剛剛開始,我建議我們選擇 Actix 或 Axum,因?yàn)樗鼈兪亲钸m合初學(xué)者的框架,而且它們有著出色的文檔。

責(zé)任編輯:武曉燕 來源: 前端柒八九
相關(guān)推薦

2024-10-18 11:48:00

2016-12-06 09:45:30

Web框架優(yōu)缺點(diǎn)

2024-07-12 05:00:00

2021-09-29 08:59:49

Rust庫(kù)編程語(yǔ)言

2024-02-01 00:21:41

Rust編程框架

2024-07-31 09:28:56

2013-05-13 01:16:15

Mobile Web webapp

2024-03-11 13:18:00

RustClap項(xiàng)目

2020-10-18 07:37:24

微前端框架前端

2021-04-30 23:19:04

前端框架工具

2015-07-14 10:11:48

前端框架語(yǔ)言

2020-02-24 19:28:23

Rust語(yǔ)言Web框架應(yīng)用領(lǐng)域

2023-01-11 09:23:50

開發(fā)框架WAF

2010-06-22 09:06:36

Visual Stud

2013-05-31 02:25:47

WebWeb設(shè)計(jì)響應(yīng)式

2018-04-23 10:05:41

Java Web框架Spring

2019-06-28 08:56:35

編程語(yǔ)言框架工具

2021-10-20 10:08:31

編程技能開發(fā)

2018-04-08 09:01:08

全閃存陣列AFA

2010-01-08 08:57:47

Windows 7上帝模式
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)