当前位置: 欣欣网 > 码农

使用 Rust Actix 快速开发高性能的Web应用

2024-05-30码农

Actix 是一个功能强大且高性能的 Rust Web 框架。本章我们将集中探讨如何在 Actix 中编写 Web 应用,并通过实例代码详细阐述各个部分的实现。

写一个简单的应用

actix-web 提供了多种原语来构建 Web 服务器和应用。它包括路由、中间件、请求前处理、响应后处理等功能。所有 actix-web 服务器都围绕 App 实例构建。 App 用于注册资源路由和中间件,并存储在相同作用域内所有处理程序之间共享的应用状态。

use actix_web::{web, App, HttpServer, Responder};
asyncfnindex() -> impl Responder {
"Hello world!"
}
#[actix_web::main]
asyncfnmain() -> std::io::Result<()> {
HttpServer::new(|| {
App::new().service(
// 在这里为所有资源和路由设置前缀 "/app"
web::scope("/app")
.route("/index.html", web::get().to(index)),
)
})
.bind(("127.0.0.1"8080))?
.run()
.await
}

在这个示例中,我们创建了一个带有前缀 /app 和资源 index.html 的应用,通过 /app/index.html URL 访问该资源。

应用状态

可以通过 web::Data<T> 提取器来访问应用状态,其中 T 代表状态数据的类型。让我们编写一个简单的应用并在状态中存储应用程序名称:

use actix_web::{get, web, App, HttpServer};
// 这个结构体代表应用状态
structAppState {
app_name: String,
}
#[get("/")]
asyncfnindex(data: web::Data<AppState>) -> String {
let app_name = &data.app_name; // 获取 app_name
format!("Hello {app_name}!")
}
#[actix_web::main]
asyncfnmain() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.app_data(web::Data::new(AppState {
app_name: String::from("Actix Web"),
}))
.service(index)
})
.bind(("127.0.0.1"8080))?
.run()
.await
}

可以在应用中注册任意数量的状态类型。

共享可变状态

HttpServer 接受一个应用工厂而不是一个应用实例。 HttpServer 为每个线程构建一个应用实例。因此,应用数据必须被多次构建。如果你想在不同线程之间共享数据,应使用可共享对象,例如 Send + Sync

在内部, web::Data 使用 Arc 。为了避免创建两个 Arc ,我们应在使用 App::app_data() 注册之前创建我们的 Data

use actix_web::{web, App, HttpServer};
use std::sync::Mutex;
structAppStateWithCounter {
counter: Mutex<i32>,
}
asyncfnindex(data: web::Data<AppStateWithCounter>) -> String {
letmut counter = data.counter.lock().unwrap();
*counter += 1;
format!("Request number: {counter}")
}
#[actix_web::main]
asyncfnmain() -> std::io::Result<()> {
let counter = web::Data::new(AppStateWithCounter {
counter: Mutex::new(0),
});
HttpServer::new(move || {
App::new()
.app_data(counter.clone())
.route("/", web::get().to(index))
})
.bind(("127.0.0.1"8080))?
.run()
.await
}


在这个示例中,我们定义了一种带有计数器的状态,并在处理程序中对其进行处理。需要注意的是,计数器状态的创建和注册是在 HttpServer::new 闭包外部完成的。

使用应用作用域来组合应用

web::scope() 方法允许设置资源组前缀。这个作用域代表一个资源前缀,将会被添加到所有资源模式中。这可以用来帮助在不同位置挂载一组路由,而保持相同资源名称。

#[actix_web::main]
asyncfnmain() {
let scope = web::scope("/users").service(show_users);
App::new().service(scope);
}

在这个示例中, show_users 路由的模式将被改为 /users/show 而不是 /show ,因为 App 的作用域前缀会添加到模式中。

应用守卫和虚拟主机

你可以将守卫视为一个简单的函数,该函数接受 request 对象引用并返回 true false 。形式上,守卫是任何实现 Guard 特征的对象。Actix Web 提供了多个守卫。

其中一个守卫是 Host ,它可以基于请求头信息作为过滤器。

#[actix_web::main]
asyncfnmain() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.service(
web::scope("/")
.guard(guard::Host("www.example.com"))
.route("", web::to(|| async { HttpResponse::Ok().body("www") })),
)
.service(
web::scope("/")
.guard(guard::Host("users.example.com"))
.route("", web::to(|| async { HttpResponse::Ok().body("user") })),
)
.route("/", web::to(HttpResponse::Ok))
})
.bind(("127.0.0.1"8080))?
.run()
.await
}

进行配置

为了简化和重用, App web::Scope 提供了 configure 方法。这个函数在将配置部分移动到不同模块或库时非常有用。

use actix_web::{web, App, HttpResponse, HttpServer};
// 这个函数可以位于不同的模块中
fnscoped_config(cfg: &mut web::ServiceConfig) {
cfg.service(
web::resource("/test")
.route(web::get().to(|| async { HttpResponse::Ok().body("test") }))
.route(web::head().to(HttpResponse::MethodNotAllowed)),
);
}
// 这个函数可以位于不同的模块中
fnconfig(cfg: &mut web::ServiceConfig) {
cfg.service(
web::resource("/app")
.route(web::get().to(|| async { HttpResponse::Ok().body("app") }))
.route(web::head().to(HttpResponse::MethodNotAllowed)),
);
}
#[actix_web::main]
asyncfnmain() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.configure(config)
.service(web::scope("/api").configure(scoped_config))
.route(
"/",
web::get().to(|| async { HttpResponse::Ok().body("/") }),
)
})
.bind(("127.0.0.1"8080))?
.run()
.await
}

这个示例的结果是:

  • / -> /

  • /app -> app

  • /api/test -> test

  • 每个 ServiceConfig 都可以有自己的 data routes services

    通过这些详尽的示例和详细的解释,相信你已经对如何在 Actix 中编写一个 Web 应用有了深入的了解。Actix 提供的各种功能和特性,使得开发和管理 Web 应用变得更加高效和简单。

    文章精选

    「Rust