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
语
言
」
学
习
资
料