21CTO导读:在过去十年左右的时间,涌现了大量Rust Web 框架。这些框架的构建考虑到了不同的用户和功能需求。当然,它们都受益于 Rust 的类型安全性、内存安全性、速度和正确性,才得以真正的流行起来。我们来一起看一看:
本文将向各位介绍五个当今最流行的 Rust Web 框架,它们分别为:
Actix Web、Rocket、Warp、Axum 和 Poem。
这些框架都为 Web 服务提供了通用元素:路由、请求处理、多种响应类型和中间件。请注意,这些框架不提供模板,模板通常由单独的扩展包处理。
Actix Web
Actix Web无疑是 Rust 最受欢迎的 Web 框架。
Actix Web 几乎满足了开发者的所有主流需求:它性能高、支持广泛的服务器功能,并且不需要太多步骤就能搭建一个基本网站。
“Actix Web”这个名字最初指的是该框架对actixActor 框架的依赖,但该框架前不久已经摆脱了这种依赖。Actix Web 的所有功能均可在稳定的 Rust 分支上使用。
以下是 Actix Web 中的一个基本的“hello world”应用程序:
use actix_web::{get, App, HttpResponse, HttpServer, Responder};
#[get("/")]
async fn hello() -> impl Responder {
HttpResponse::Ok().body("Hello world!")
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| App::new().service(hello))
.bind(("127.0.0.1", 8080))?
.run()
.await
}
解释
get()函数上的属性表示hello()它要服务的路由,但只有在App使用.service()方法将其添加到对象中后,它才会处于激活状态。Actix Web 还支持更高级的路由构造,例如,您可以从 URL 中捕获位置变量,并使用这些变量将请求路由到不使用的函数get()中。
性能是 Actix Web 的一大吸引力,所有请求和响应都作为不同类型处理。服务器使用线程池来处理请求,线程之间不共享任何内容以最大程度地提高性能。如果需要,您可以使用手动共享状态Arc,但是 Actix Web 的维护者敦促不要做任何会阻塞工作线程,进而破坏性能的事情。对于长时间运行的非 CPU 绑定工作,请开发者使用 Future 或 async。
Actix Web 还提供了基于类型的错误代码处理程序,并使用内置中间件系统来实现日志记录。此框架还包括一个通用的用户会话管理系统,其中 Cookie 是默认存储类型,但您可以根据需要添加其它存储类型。静态文件和目录也可以使用其自己的专用处理程序来做为服务提供。
Actix Web 捆绑了许多常见的 Web 服务功能,还有一些特殊的功能。这些功能包括处理表单的 URL 编码主体、自动升级到 HTTPS/2、解压缩 Brotli、、gzip和deflate压缩zstd数据以及处理分块编码。
对于 WebSockets,Actix Web 需要 crate actix-web-actors,这是它的额外主依赖项。同样,对于多字符流块处理,您需要 crate actix-multipart。(对于 JSON 的转换,Actix Web 使用serde和serde_json,Rust 用户通常会很熟悉。)
Actix Web 在 2020 年就引起了人们的关注,当时其原始作者退出了该项目,据称是因为对其unsafe代码使用的批评。然而,其他主要维护者继续开发该框架,此后几年它一直在不断发展,大部分unsafe代码已被删除。
Rocket
Rocket在 Rust Web 框架中最大的特色,是它能让你用最少的代码获得最多的结果。
用 Rocket 编写一个基本的 Web 应用程序只需要很少的代码和很少的繁琐步骤。Rocket 通过使用 Rust 的类型系统来描述许多行为来实现这些,因此它们可以在编译时被强制执行与编码。
这是 Rocket 中的一个基本的“hello world”应用程序:
#[macro_use] extern crate rocket;
#[get("/")]
fn hello_world() -> &'static str {
"Hello, world!"
}
#[launch]
fn rocket() -> _ {
rocket::build().mount("/", routes![hello_world])
}
解释
可以看到,Rocket 通过使用属性来简洁地工作。路由使用属性来修饰它们使用的方法和 URL 模式。正如您在本例中看到的,属性#[launch]表示用于挂载路由并设置应用程序以监听请求的函数。
尽管“hello world”示例中的路由是同步的,但 Rocket 中的路由可以做为异步,并且通常应该尽可能地异步。默认情况下,Rocket 使用 tokio运行时来处理诸如将同步操作转换为异步。
Rocket 提供了很多处理请求的常用功能,例如从 URL 元素中提取变量。一个独特的功能是“请求保护”,您可以使用实现 RocketFromRequest特征的 Rust 类型来描述路由的验证策略。
例如,您可以创建自定义类型来阻止路由被触发,比如除非请求标头中存在某些信息并且可以验证(例如具有特定权限的 Cookie)。这样您就可以将权限之类的东西构建到 Rust 编译时的类型安全里。
Rocket 的另一个有用且独特的功能是fairings,也就是 Rocket 的中间件版本。实现该Fairing特征的类型可用于向事件(如请求或响应)添加回调。但 fairings 无法更改或停止请求。
为此,fairing 最适合具有全局行为的事物 — 日志记录、收集性能指标或整体安全策略。对于身份验证等操作,请使用请求保护。
Warp
Warp与其他 Rust Web 框架的最大区别在于它使用可组合组件的方式。用 Warp 的术语来说就是“过滤器” ,这些组件可以链接在一起来创建服务。
Warp 中基本的“hello world”并不能很好地展示这个特性,但它值得展示一下这个框架是多么地简洁:
use warp::Filter;
#[tokio::main]
async fn main() {
let hello = warp::path!().map(|| "Hello world");
warp::serve(hello).run(([127, 0, 0, 1], 8080)).await;
}
解释
Filter
Filter(即过滤器)实现了一些独立特征,每个过滤器都能够将输出传递给另一个过滤器以修改行为。在下面示例中,warp::path是一个可以链接到其他操作(例如.map()应用函数)的过滤器。
Warp 文档中的另一个示例更详细地展示了过滤系统:
use warp::Filter;
let hi = warp::path("hello")
.and(warp::path::param())
.and(warp::header("user-agent"))
.map(|param: String, agent: String| {
format!("Hello {}, whose agent is {}", param, agent)
});
解释
在这里,多个过滤器按以下顺序链接在一起以创建以下行为:
-
设置路径为hello的节点。
-
在路径末尾添加一个参数,因此路径须采用的形式为:
-
/hello/。( .and() 方法是 Warp 中组合的工作方式之一。)
-
为HTTP标头添加一个解析器user-agent,这样user-agent就不会处理任何没有标头的传入请求。
-
将format!带有参数param(收集的参数)和agent(user-agent字符串)的宏应用于字符串,并将其返回给客户端。
喜欢组合方法的开发者会喜欢 Warp 补充他们的工作方式。
组合方法的一个结果是,你可以用各种不同的方式做同一件事,但是注意并非所有方式都是直观的。值得一看的是 Warp 存储库中的示例,以了解使用 Warp 解决常见编程场景的不同方法。
另一个结果来自过滤器在编译时的工作方式。从许多不同的过滤器中组合许多路由,会延长编译时间,当然这些路由在运行时速度很快。另一种选择是使用动态调度来处理具有许多路由,但运行时性能会略有下降。
Axum
Axum框架建立在tower之上,适用于各种客户端/服务器应用程序以及tokio异步应用程序的 crate 生态系统。如果您已经体验过tower或在相关项目中使用过 Axum,那么使用 Axum 会更容易。
这是 Axum 开发文档中的基本 Axum “hello world” 应用程序。
use axum::{
routing::get,
Router,
};
#[tokio::main]
async fn main() {
let app = Router::new().route("/", get(|| async { "Hello, World!" }));
let listener = tokio::net::TcpListener::bind("127.0.0.1:8080").await.unwrap();
axum::serve(listener, app).await.unwrap();
}
解释
Axum 使用与 Actix 相同的许多模式来处理路由和处理程序的工作方式。路由处理程序函数Router通过.route()方法添加到对象中,axum::extract模块包含用于提取 URL 组件或POST有效负载的类型。响应实现特征,towertower::Service Error 错误通过自己的类型IntoResponse处理。
最后的行为依赖于tower关键的 Axum 组件,还包括 Axum 如何处理中间件。路由器、方法和单个处理程序都可以通过对象.layer中的不同方法应用中间件tower。还可以使用tower::ServiceBuilder创建多个层的聚合并将它们一起应用。
Axum 为 Web 服务中的其他常见模式提供了自己的工具。例如,可以使用类型以类型安全的方式在处理程序之间共享状态。可以在Axum 的示例目录中找到实现正常关闭或设置数据库连接State等典型场景的方法。
Poem
大多数编程语言都有一个功能齐全的“极简主义” Web 框架(例如Python中的Django),还有一个小型、简洁的“极简主义” Web 框架(例如Python 中的Bottle)。
这个Poem是 Rust 的最小版本,默认情况下仅提供足够的功能来支持基本的 Web 服务。
下面是一个“hello world”示例,当它包含在 URL 中时会显示用户名:
use poem::{get, handler, listener::TcpListener, web::Path, Route, Server};
#[handler]
fn hello(Path(name): Path) -> String {
format!("hello: {}", name)
}
#[tokio::main]
async fn main() -> Result<(), std::io::Error> {
let app = Route::new().at("/hello/:name", get(hello));
Server::new(TcpListener::bind("0.0.0.0:3000"))
.run(app)
.await
}
解释
该应用程序中的许多功能应该与您迄今为止见过的其他框架和示例中的功能相似:比如设置路由、将 URL 和处理程序绑定它们、从请求中提取元素等等。
为了缩短编译时间,Poem 默认不安装对某些功能的支持。Cookie、CSRF 投影、HTTP over TLS、WebSockets、国际化以及请求/响应压缩和解压缩(仅举几例)都需要手动启用。
尽管 Poem 非常简单,但它仍然具有很多实用功能。它包含大量常用、有用的中间件,您也可以轻松地实现自己的中间件。一个贴心的便利是NormalizePath,它是一种使请求路径一致的机制。这包括一个用于处理 URL 中尾部斜杠的通用处理程序。使用该处理程序,您可以一次性在整个应用程序中实现一致地的格式。
Poem 的示例目录比您看到的其他一些框架要小,但它主要关注需要详细文档的示例,例如将 Poem 与AWS Lambda一起使用,或生成符合 OpenAPI 规范的 API。
哪个 Rust 框架最适合您?
总体来讲,Actix Web 是一种良好且平衡的解决方案,尤其是在以性能为目标的情况下。而 Rocket 可让您保持代码简短但又富有表现力,其“整流罩”系统为实现中间件行为提供了强大的支持。
喜欢使用可组合元素的开发者会想要尝试 Warp,因为它可以让你以编程方式构建具有极强表现力的路由或工作流。Axum 最直接的吸引力在于已经熟悉生态系统的 Rust tower用户,因为它足够实用,因此并不局限于该受众。Poem 框架很简单,如果你只需要最基本的路由和请求处理,那么它就很适合你。
以上框架介绍完毕,祝您Rust愉快~