前外交部副部长傅莹:一旦中美闹翻,有没有国家会站在中国一边

【少儿禁】马建《亮出你的舌苔或空空荡荡》

【成都mc是什么】成都mc浴室小黑屋见闻(史诗级巨瓜)

全球化丧钟,不仅为张一鸣而鸣

到底是谁杀害了在非洲的中国人

生成图片,分享到微信朋友圈

自由微信安卓APP发布,立即下载! | 提交文章网址
查看原文

在2022年选择哪个Rust web框架?

李明 coding到灯火阑珊 2022-12-04


框架

crate

版本

下载量

描述

actix-web

4.2.1

5,134,720

Actix Web是Rust的一个功能强大、实用且速度极快的Web框架

warp

0.3.3

4,114,095

Warp是一个超简单、可组合的web服务器框架。

axum

0.5.16

235,150

关注人体工程学和模块化的Web框架(作者:tokio团队)


还有很多其他的框架,比如:
  • poem

  • hyper

  • rocket

  • tide

  • rouille

  • iron

  • ...



性能
对于大多数用例,所有这些框架都具有足够好的性能。因此,我们不会把时间花在过度优化的微观基准上,如果你有特定的需求,比如在一台机器上每秒处理数百万个请求,那么就让使用特定的设置执行自己的基准测试。


生态和社区

一个好的web框架既需要一个好的社区来帮助你,通过第三方包的使用方式来直接使用它们,以节省你的时间或寻找灵感。

actix的性能因其在TechEmpower Web框架基准测试中而闻名,是拥有最大生态系统和社区的框架。也就是说,axum是tokio项目的一部分,因此受益于其庞大的生态系统和社区。


JSON反序列化
actix-web
 1#[derive(Debug, Serialize, Deserialize)]
2struct Hello {
3    message: String,
4}
5
6async fn index(item: web::Json<Hello>) -> HttpResponse {
7    HttpResponse::Ok().json(item.message) // <- send response
8}
9
10#[actix_web::main]
11async fn main() -> std::io::Result<()> {
12    HttpServer::new(|| {
13        App::new()
14            .service(web::resource("/").route(web::post().to(index)))
15    })
16    .bind(("127.0.0.1", 8080))?
17    .run()
18    .await
19}


warp
 1#[derive(Debug, Serialize, Deserialize)]
2struct Hello {
3    message: String,
4}
5
6async fn index(item: Hello) -> Result<impl warp::Reply, Infallible> {
7    Ok(warp::reply::json(&hello.message))
8}
9
10#[tokio::main]
11async fn main() {
12    let promote = warp::post()
13        .and(warp::body::json())
14        .map(index);
15
16    warp::serve(promote).run(([127, 0, 0, 1], 8080)).await
17}


axum
 1#[derive(Debug, Serialize, Deserialize)]
2struct Hello {
3    message: String,
4}
5
6async fn index(item: Json<Hello>) ->impl IntoResponse { {
7    Json(item.message)
8}
9
10#[tokio::main]
11async fn main() {
12    let app = Router::new().route("/", post(index));
13
14    let addr = SocketAddr::from(([127, 0, 0, 1], 8080));
15    axum::Server::bind(&addr)
16        .serve(app.into_make_service())
17        .await
18        .unwrap();
19}


所有框架都使用泛型来提供简单的JSON反序列化。axum和actix-web在使用它们的辅助工具自动提取body的数据时更加直接。


路由
actix-web
1fn main() {
2    App::new()
3        .service(web::resource("/").route(web::get().to(api::list)))
4        .service(web::resource("/todo").route(web::post().to(api::create)))
5        .service(web::resource("/todo/{id}")
6          .route(web::post().to(api::update))
7          .route(web::delete().to(api::delete)),
8        );
9}


warp
 1pub fn todos() -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
2    todos_list(db.clone())
3        .or(todos_create(db.clone()))
4        .or(todos_update(db.clone()))
5        .or(todos_delete(db))
6}
7
8pub fn todos_list() -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
9    warp::path!("todos")
10        .and(warp::get())
11        .and(warp::query::<ListOptions>())
12        .and_then(handlers::list_todos)
13}
14
15pub fn todos_create() -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
16    warp::path!("todos")
17        .and(warp::post())
18        .and(json_body())
19        .and_then(handlers::create_todo)
20}
21
22pub fn todos_update() -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
23    warp::path!("todos" / u64)
24        .and(warp::put())
25        .and(json_body())
26        .and_then(handlers::update_todo)
27}
28
29pub fn todos_delete() -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
30    warp::path!("todos" / u64)
31        .and(warp::delete())
32        .and_then(handlers::delete_todo)
33}
34
35fn main() {
36  let api = filters::todos(db);
37  warp::serve(api).run(([127, 0, 0, 1], 8080)).await
38}


axum
1let app = Router::new()
2    .route("/todos", get(todos_list).post(todos_create))
3    .route("/todos/:id", patch(todos_update).delete(todos_delete));


Axum无疑是赢家,紧随其后的是actix-web。然后是warp,它有一个支持复合的函数API,与我们通常期望的web框架相差甚远。


中间件
actix-web
 1pub struct SayHi;
2
3impl<S, B> Transform<S, ServiceRequest> for SayHi
4where
5    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
6    S::Future: 'static,
7    B: 'static,
8{
9    type Response = ServiceResponse<B>;
10    type Error = Error;
11    type InitError = ();
12    type Transform = SayHiMiddleware<S>;
13    type Future = Ready<Result<Self::Transform, Self::InitError>>;
14
15    fn new_transform(&self, service: S) -> Self::Future {
16        ready(Ok(SayHiMiddleware { service }))
17    }
18}
19
20pub struct SayHiMiddleware<S> {
21    service: S,
22}
23
24impl<S, B> Service<ServiceRequest> for SayHiMiddleware<S>
25where
26    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
27    S::Future: 'static,
28    B: 'static,
29{
30    type Response = ServiceResponse<B>;
31    type Error = Error;
32    type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
33
34    dev::forward_ready!(service);
35
36    fn call(&self, req: ServiceRequest) -> Self::Future {
37        println!("before");
38
39        let fut = self.service.call(req);
40
41        Box::pin(async move {
42            let res = fut.await?;
43            println!("after");
44            Ok(res)
45        })
46    }
47}
48
49#[actix_web::main]
50async fn main() -> std::io::Result<()> {
51    App::new()
52        .wrap(simple::SayHi)
53        .service(
54            web::resource("/").to(|| async {
55                "Hello, middleware! Check the console where the server is run."
56            }),
57        )
58}


warp
 1pub fn json_body<T: DeserializeOwned + Send>() -> impl Filter<Extract = (T,), Error = warp::Rejection> + Clone {
2    warp::body::content_length_limit(1024 * 16).and(warp::body::json())
3}
4
5
6fn main() {
7    let api = api.and(warp::path("jobs"))
8      .and(warp::path::end())
9      .and(warp::post())
10      .and(json_body())
11      .and_then(create_job);
12}


axum
 1#[derive(Clone)]
2struct MyMiddleware<S> {
3    inner: S,
4}
5
6impl<S> Service<Request<Body>> for MyMiddleware<S>
7where
8    S: Service<Request<Body>, Response = Response> + Clone + Send + 'static,
9    S::Future: Send + 'static,
10{
11    type Response = S::Response;
12    type Error = S::Error;
13    type Future = BoxFuture<'static, Result<Self::Response, Self::Error>>;
14
15    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
16        self.inner.poll_ready(cx)
17    }
18
19    fn call(&mut self, mut req: Request<Body>) -> Self::Future {
20        println!("before");
21        // best practice is to clone the inner service like this
22        // see https://github.com/tower-rs/tower/issues/547 for details
23        let clone = self.inner.clone();
24        let mut inner = std::mem::replace(&mut self.inner, clone);
25
26        Box::pin(async move {
27            let res: Response = inner.call(req).await?;
28
29            println!("after");
30
31            Ok(res)
32        })
33    }
34}
35
36fn main() {
37  let app = Router::new()
38    .route("/", get(|| async { /* ... */ }))
39    .layer(layer_fn(|inner| MyMiddleware { inner }));
40}


毫无疑问,warp更好。


状态

在构建web服务时,需要为外部服务共享一些变量,例如数据库连接池或一些服务的客户端。

actix-web
 1struct State {}
2
3async fn index(
4    state: Data<Arc<State>>,
5    req: HttpRequest,
6) -> HttpResponse {
7  // ...
8}
9
10#[actix_web::main]
11async fn main() -> io::Result<()> {
12    let state = Arc::new(State {});
13
14    HttpServer::new(move || {
15        App::new()
16            .app_data(state.clone())
17            .service(web::resource("/").to(index))
18    })
19    .bind(("127.0.0.1", 8080))?
20    .run()
21    .await
22}


warp
 1struct State {}
2
3pub fn with_state(
4    state: Arc<State>,
5) -> impl Filter<Extract = (Arc<State>,), Error = std::convert::Infallible> + Clone {
6    warp::any().map(move || state.clone())
7}
8
9pub async fn create_job(
10    state: Arc<AppState>,
11) -> Result<impl warp::Reply, warp::Rejection> {
12    // ...
13}
14
15fn main() {
16    let state = Arc::new(State{});
17    let api = api.and(warp::path("jobs"))
18      .and(warp::path::end())
19      .and(warp::post())
20      .and(with_state(state))
21      .and_then(create_job);
22}


axum
 1struct State {}
2
3async fn handler(
4    Extension(state): Extension<Arc<State>>,
5) {
6    // ...
7}
8
9fn main() {
10    let shared_state = Arc::new(State {});
11
12    let app = Router::new()
13        .route("/", get(handler))
14        .layer(AddExtensionLayer::new(shared_state));
15}




这是一个平局,所有框架的人体工程学都非常相似。


总结

我最倾向于axum,我发现它有最干净的API,它构建在hyper之上,hyper是Rust中测试最多、最可靠的HTTP栈,而且因为它是由tokio的团队开发的。

对于大型项目,我认为actix-web是无可争议的赢家。

对于较小的项目(多达30/40条路由),warp是非常好的,它也是构建在hyper之上的,因此受益于hyper的可靠性和性能。


本文翻译自:
https://dev.to/sylvainkerkour/which-rust-web-framework-to-choose-in-2022-with-code-examples-494n



文章有问题?点此查看未经处理的缓存