你应该知晓的Rust Web 框架(一)

简介: 你应该知晓的Rust Web 框架

要相信信念的力量

大家好,我是柒八九

前言

在之前的用 Rust 搭建 React Server Components 的 Web 服务器我们利用了Axum构建了RSC的服务器。也算是用Rust在构建Web服务上的小试牛刀。

虽然说AxumRust Web应用中一枝独秀。但是,市面上也有很多不同的解决方案。所以,今天我们就比较一些 Rust 框架,突出它们各自的优势和缺点,以帮助我们为项目做出明智的决策。没有对比就没有选择,我们只有在真正的了解各个框架的优缺点和适应场景,在以后的开发中才能有的放矢的放心选择。

文本中,我们会介绍很多Rust框架。并且会按照如下的受欢迎程度的顺序来讲。

image.png

好了,天不早了,干点正事哇。


我们能所学到的知识点

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

1. Axum

AxumRust 生态系统中具有特殊地位的 Web 应用程序框架(从下载量就可见端倪)。它是 Tokio 项目的一部分,Tokio 是使用 Rust 编写异步网络应用程序的运行时Axum 不仅使用 Tokio 作为其异步运行时,还与 Tokio 生态系统的其他库集成,利用 Hyper 作为其 HTTP 服务器和 Tower 作为中间件。通过这样做,我们能够重用 Tokio 生态系统中现有的库和工具。

Axum不依赖于宏,而是利用 Rust类型系统提供安全且人性化的 API。这是通过使用特性来定义框架的核心抽象实现的,例如 Handler 特性,用于定义应用程序的核心逻辑。这种方法允许我们轻松地从较小的组件中组合应用程序,这些组件可以在多个应用程序中重用。

Axum 中,处理程序(handler)是一个接受请求并返回响应的函数。这与其他后端框架类似,但使用 AxumFromRequest 特性,我们可以指定从请求中提取的数据类型。返回类型需要实现 IntoResponse 特性(trait),已经有许多类型实现了这个特性,包括允许轻松更改响应的状态代码的元组类型。

Rust 的类型系统、泛型,尤其是在traits中使用异步方法(或更具体地说是返回的 Future),当不满足trait限制时,Rust 的错误消息会很复杂。特别是当尝试匹配抽象trait限制时,经常会得到一堆难以解读的文本。为此Axum 提供了一个带有辅助宏的库,将错误放到实际发生错误的地方,使得更容易理解发生了什么错误。

虽然Axum 做了很多正确的事情,可以很容易地启动执行许多任务的应用程序。但是,有一些事情需要特别注意。Axum版本仍然低于 1.0,也就意味着Axum 团队保留在版本之间根本性地更改 API 的自由,这可能导致我们的应用程序出现严重问题。


Axum 示例

下面展示了一个 WebSocket 处理程序,它会回显收到的任何消息。

// #[tokio::main] 宏标记了 `main` 函数,表明这是一个异步的`Tokio`应用程序。
#[tokio::main]
async fn main() {
    // 首先创建了一个 `TcpListener` 监听器,绑定到地址 "127.0.0.1:3000" 上
    // 然后,通过 `await` 等待监听器绑定完成
    // 如果绑定失败,会通过 `unwrap` 方法抛出错误。
    let listener = tokio::net::TcpListener::bind("127.0.0.1:3000")
        .await
        .unwrap();
    println!("listening on {}", listener.local_addr().unwrap());
    // 使用 `axum::serve` 启动 Axum 框架的服务器,
    //    监听前面创建的 `TcpListener`。
    // `app()` 函数返回的是一个 `Router`
    //    它定义了一个简单的路由,将路径 "/a" 映射到处理函数 `a_handler`。
    axum::serve(listener, app()).await.unwrap();
}
// 返回一个 `Router`,它只有一个路由规则,
//    将 "/a" 路径映射到 `a_handler` 处理函数
fn app() -> Router {
    Router::new()
        .route("/a", get(a_handler))
}
// 一个WebSocket处理程序,它会回显收到的任何消息。
// 定义为一个WebSocket处理程序,
//   它接收一个 `WebSocketUpgrade` 参数,表示WebSocket升级。
async fn a_handler(ws: WebSocketUpgrade) -> Response {
    // 调用将WebSocket升级后的对象传递给 `a_handle_socket` 处理函数。
    ws.on_upgrade(a_handle_socket)
}
async fn a_handle_socket(mut socket: WebSocket) {
    // 使用 while let 循环,持续从 WebSocket 连接中接收消息。 
    // socket.recv().await 通过异步的方式接收消息,返回一个 Result,
    // 其中 Ok(msg) 表示成功接收到消息。
    while let Some(Ok(msg)) = socket.recv().await {
        //  使用 if let 匹配,判断接收到的消息是否为文本消息。
        //  WebSocket消息可以是不同类型的,这里我们只处理文本消息。
        if let Message::Text(msg) = msg {
            // 构造一个回显消息,将客户端发送的消息包含在回显消息中。
            // 然后,使用 socket.send 方法将回显消息发送回客户端。
            // await 等待发送操作完成。
            if socket
                .send(Message::Text(format!("You said: {msg}")))
                .await
                // 检查 send 操作是否返回错误。
                // 如果发送消息出现错误(例如,连接断开),
                // 就通过 break 跳出循环,结束处理函数。
                .is_err()
            {
                break;
            }
        }
    }
}

Axum 特点

  • 无宏 API。
  • 利用 TokioTowerHyper 构建强大的生态系统。
  • 出色的开发体验。
  • 仍处于 0.x 版本,因此可能发生重大变更。

2. Actix Web

Actix WebRust 中存在已久且非常受欢迎的 Web 框架之一。像任何良好的开源项目一样,它经历了许多迭代,但已经达到了主要版本(不再是 0.x),换句话说:在主要版本内,它可以确保没有破坏性的更改。

乍一看,Actix WebRust 中的其他 Web 框架非常相似。我们使用来定义 HTTP 方法路由(类似于 Rocket),并使用提取器(extractors)从请求中获取数据(类似于 Axum)。与 Axum 相比,它们之间的相似之处显著,甚至在它们命名概念和特性的方式上也很相似。最大的区别是 Actix Web 没有将自己与Tokio 生态系统强关联在一起。虽然 Tokio 仍然是 Actix Web 底层的运行时,但是该框架具有自己的抽象和特性,以及自己的一套 crates 生态系统。这既有利有弊。一方面,我们可以确保事物能够很好地配合使用,另一方面,我们可能会错失 Tokio 生态系统中已经可用的许多功能。

Actix Web 实现了自己的 Service 特性,它基本上与 TowerService 相同,但仍然不兼容。这意味着在 Tower 生态系统中大多数可用的中间件在 Actix 中不可用。

如果在 Actix Web 中需要实现一些特殊任务,而需要自己实现,我们可能会碰到运行框架中的 Actor 模型。这可能会增加一些意想不到的问题。

Actix Web 社区很给力。该框架支持 HTTP/2WebSocket 升级,提供了用于 Web 框架中最常见任务的 crates 和指南,以及出色的文档,而且速度很快。Actix Web 之所以受欢迎,是有原因的,如果我们需要保证版本,请注意它可能是我们目前的最佳选择


Actix Web 示例

Actix Web 中,一个简单的 WebSocket 回显服务器如下所示:

use actix::{Actor, StreamHandler};
use actix_web::{
  web, 
  App, 
  Error, 
  HttpRequest, 
  HttpResponse, 
  HttpServer
};
use actix_web_actors::ws;
/// 定义HTTP Actor
// 定义了一个名为 MyWs 的结构体,这将用作WebSocket的Actix Actor。
// Actors 是Actix框架中的并发单元,用于处理异步消息
struct MyWs;
// 为 MyWs 结构体实现了 Actor trait,指定了 WebsocketContext 作为上下文类型。
impl Actor for MyWs {
    type Context = ws::WebsocketContext<Self>;
}
/// 处理ws::Message消息的处理程序
// 为 MyWs 结构体实现了 StreamHandler trait,处理WebSocket连接中的消息。
impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for MyWs {
    // 对接收到的不同类型的消息进行处理。例如,对于 Ping 消息,发送 Pong 消息作为响应。
    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),
            _ => (),
        }
    }
}
// 定义了一个处理HTTP请求的异步函数。
async fn index(req: HttpRequest, stream: web::Payload) -> Result<HttpResponse, Error> {
    // 将WebSocket连接升级,并将请求委托给 MyWs Actor 处理。
    let resp = ws::start(MyWs {}, &req, stream);
    println!("{:?}", resp);
    resp
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
    //  创建了一个 HttpServer 实例,通过 App::new() 创建一个应用,
    // 该应用只有一个路由,将路径 "/ws/" 映射到处理函数 index 上。
    HttpServer::new(|| App::new().route("/ws/", web::get().to(index)))
        // 绑定服务器到地址 "127.0.0.1" 和端口 8080。
        .bind(("127.0.0.1", 8080))?
        // 启动服务器并等待其完成运行。
        .run()
        .await
}

Actix Web 特点

  • 拥有强大的生态系统。
  • 基于 Actor 模型。
  • 通过主要版本保证的稳定 API。
  • 出色的文档。

3. Rocket

RocketRust Web 框架生态系统中已经有一段时间了:它的主要特点是基于宏的路由内置表单处理、对数据库和状态管理的支持,以及其自己版本的模板!Rocket 确实尽力做到构建 一个 Web 应用程序所需的一切。

然而,Rocket 的雄心壮志也带来了一些代价。尽管仍在积极开发中,但发布的频率不如以前。这意味着框架的用户会错过许多重要的东西。

此外,由于其一体化的方法,我们还需要了解 Rocket 的实现方式。Rocket 应用程序有一个生命周期,构建块以特定的方式连接,如果出现问题,我们需要理解问题出在哪里。

Rocket 是一个很棒的框架,如果我们想开始使用 Rust 进行 Web 开发,它是一个很好的选择。对于我们许多人来说,Rocket 是进入 Rust 的第一步,使用它仍然很有趣。

Rocket 示例

处理表单的 Rocket 应用程序的简化示例:

// 定义了一个名为 Password 的结构体,该结构体派生了 Debug 和 FromForm traits。
// FromForm trait 用于从表单数据中提取数据。
// 该结构体包含两个字段 first 和 second,分别表示密码的第一个和第二个部分。
#[derive(Debug, FromForm)]
struct Password<'v> {
    // 表示对字段的长度进行了验证,要求长度在6个字符以上
    #[field(validate = len(6..))]
    // 表示第一个字段必须等于第二个字段
    #[field(validate = eq(self.second))]
    first: &'v str,
    //  表示第二个字段必须等于第一个字段。
    #[field(validate = eq(self.first))]
    second: &'v str,
}
// 省略其他结构体和实现...
// 定义了一个处理GET请求的函数 index,返回一个 Template 对象。
// 这个函数用于渲染首页。
#[get("/")]
fn index() -> Template {
    Template::render("index", &Context::default())
}
// 定义了一个处理POST请求的函数 submit。
// 这个函数接受一个 Form 对象,其中包含了表单的数据
#[post("/", data = "<form>")]
fn submit(form: Form<Submit<'_>>) -> (Status, Template) {
    // 通过检查 form.value 是否包含 Some(ref submission) 来判断表单是否提交。
    let template = match form.value {
        // 如果提交了表单,打印提交的内容,并渲染 "success" 页面;
        Some(ref submission) => {
            println!("submission: {:#?}", submission);
            Template::render("success", &form.context)
        }
        // 否则,渲染 "index" 页面。
        None => Template::render("index", &form.context),
    };
    (form.context.status(), template)
}
// 定义了启动Rocket应用程序的函数。
#[launch]
fn rocket() -> _ {
    // 使用 rocket::build() 创建一个Rocket应用程序实例
    rocket::build()
        // 并通过 .mount() 方法挂载路由。
        // routes![index, submit] 定义了两个路由,
        // 分别映射到 index 和 submit 函数。
        .mount("/", routes![index, submit])
        // 添加了一个模板处理的Fairing(Rocket中的中间件)
        .attach(Template::fairing())
        // 将静态文件服务挂载到根路径。
        .mount("/", FileServer::from(relative!("/static")))
}

Rocket 特点

  • 一体化的方法。
  • 出色的开发体验。
  • 开发活跃度不如以前。
  • 初学者的绝佳选择。
相关文章
|
5天前
|
开发框架 JSON API
震撼发布!Python Web开发框架下的RESTful API设计全攻略,让数据交互更自由!
【7月更文挑战第22天】在Python Web开发中,设计高效的RESTful API涉及选择框架(如Flask或Django)、明确资源及使用HTTP方法(GET, POST, PUT, DELETE)来操作数据。响应格式通常是JSON,错误处理也很重要。示例展示了使用Flask创建图书管理API,包括版本控制、文档化、安全性和性能优化是最佳实践。这样的API使数据交互更顺畅。
26 2
|
2天前
|
JSON API 网络架构
揭秘!Python Web框架下的RESTful API设计,为何成为互联网新宠?
【7月更文挑战第24天】在互联网快速发展背景下,RESTful API以其简洁性和可扩展性成为应用间通信的首选。REST强调资源为核心,利用HTTP方法处理CRUD操作,促进前后端分离,提高开发效率。Python的Flask框架支持轻松构建RESTful API,通过路由、请求和响应机制实现。示例代码展示了如何使用Flask创建、读取和更新用户信息。Python Web框架因其丰富的工具和库,简化RESTful API开发,适应微服务架构需求,在互联网发展中扮演重要角色。
|
6天前
|
移动开发 小程序 JavaScript
转Web运行时的“框架”模块主要负责什么功能
转Web运行时的“框架”模块主要负责什么功能
|
6天前
|
API 网络架构 开发者
探索Python Web框架的核心:路由机制详解
【7月更文挑战第21天】Python Web开发中,Flask和Django的路由机制解析:Flask用@app.route装饰器绑定URL到视图,如`@app.route(&#39;/user/&lt;username&gt;&#39;)`;Django通过urls.py配置URL模式,如`path(&#39;user/&lt;str:username&gt;/&#39;, views.user_profile, name=&#39;user_profile&#39;)`。两者都支持静态、动态路由和HTTP方法绑定,展现路由灵活性,助力构建高效Web应用。
13 0
|
6天前
|
缓存 监控 安全
中间件在Python Web框架中的角色与应用场景
【7月更文挑战第21天】中间件在Python Web开发中作为服务器与应用间的软件层,拦截、处理请求和响应,无需改动应用代码。它扩展框架功能,复用跨应用逻辑,加强安全,优化性能。如Django中间件处理请求/响应,Flask通过WSGI中间件实现类似功能,两者均在不触及核心代码前提下,灵活增强应用行为,是现代Web开发关键组件。
|
6天前
|
前端开发 开发者 Python
从零到一:Python Web框架中的模板引擎入门与进阶
【7月更文挑战第20天】模板引擎如Jinja2在Python Web开发中连接后端与前端,提高代码可读性和协作效率。Flask默认集成Jinja2,提供条件语句、循环、宏和模板继承等功能。例如,创建一个简单Flask应用,渲染&quot;Hello, World!&quot;,并展示如何使用条件语句和循环处理数据。通过宏定义重用代码,模板继承实现页面结构共享。学习模板引擎能提升开发效率和项目质量。
17 0
|
6天前
|
中间件 数据库 开发者
解析Python Web框架的四大支柱:模板、ORM、中间件与路由
【7月更文挑战第20天】Python Web框架如Django、Flask、FastAPI的核心包括模板(如Django的DTL和Flask的Jinja2)、ORM(Django的内置ORM与Flask的SQLAlchemy)、中间件(Django的全局中间件与Flask的装饰器实现)和路由(Django的urls.py配置与Flask的@app.route()装饰器)。这些组件提升了代码组织和数据库操作的便捷性,确保了Web应用的稳定性和可扩展性。
|
1月前
|
Rust 安全 开发者
探索Rust语言的内存安全特性
【6月更文挑战第8天】Rust语言针对内存安全问题提供了创新解决方案,包括所有权系统、借用规则和生命周期参数。所有权系统确保值与其所有者绑定,防止内存泄漏;借用规则保证同一时间只有一个可变引用或多个不可变引用,消除数据竞争和野指针;生命周期参数则强化了引用的有效范围,提升安全性。通过这些特性,Rust帮助开发者编写出更健壮、安全的高性能软件,有望成为系统编程领域的领头羊。
|
1月前
|
机器学习/深度学习 Rust 安全
Rust语言:为何备受开发者青睐?
Rust编程语言以其内存安全、高性能、并发编程支持和强大社区获得青睐。作为系统编程语言,Rust的所有权与借用检查机制确保了内存安全,适用于高可靠性系统。它拥有接近C/C++的运行时性能,适合游戏开发和数据分析。Rust的并发特性包括轻量级线程和原子操作,便于构建高性能并发系统。活跃的社区和完善的生态系统,如丰富的库和框架,加速了开发者的学习和项目开发进程。【6月更文挑战第3天】
57 3
|
29天前
|
Rust 安全 开发者
Rust语言的Hello, World! 程序解析
Rust语言的Hello, World! 程序解析
21 0