actix-web
提供了多种原语以使用Rust构建Web服务器和应用程序。
它提供路由,中间件,请求的预处理,响应的后处理等。
所有actix-web
服务器均围绕App
实例构建。
它用于注册resources(资源)和middlewares(中间件)的路由。
它还存储同一scope(范围/作用域)内所有处理程序之间共享的应用程序状态。
应用程序的scope
充当所有路由的命名空间,即特定应用程序作用域的所有路由都具有相同的url路径前缀。
应用程序前缀始终包含一个“/”斜杠。
如果提供的前缀不包含斜杠,则会自动将其插入。
前缀应包含值路径段。
对于范围为
/app
的应用程序 路径为/app
,/app/
, 或/app/test
的任何请求都将匹配; 但是,路径/application
不匹配。
use actix_web::{web, App, Responder, HttpServer};
async fn index() -> impl Responder {
"Hello world!"
}
#[actix_rt::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new().service(
web::scope("/app").route("/index.html", web::get().to(index)),
)
})
.bind("127.0.0.1:8088")?
.run()
.await
}
在此示例中,将创建一个带有/app
前缀和index.html
resource(资源)的应用程序。
该resource可通过/app/index.html
网址获得。
有关更多信息,请查看URL Dispatch部分。
应用程序状态与同一scope(范围)内的所有路由和resources(资源)共享。
可以使用web::Data<T>
提取器访问状态,其中T
是状态类型。
状态也可用于中间件。
让我们编写一个简单的应用程序,并将应用程序名称存储在以下状态:
use actix_web::{web, App, HttpServer};
use std::sync::Mutex;
// This struct represents state
struct AppState {
app_name: String,
}
async fn index(data: web::Data<AppState>) -> String {
let app_name = &data.app_name; // <- get app_name
format!("Hello {}!", app_name) // <- response with app_name
}
并在初始化应用程序时传递状态,然后启动应用程序:
#[actix_rt::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.data(AppState {
app_name: String::from("Actix-web"),
})
.route("/", web::get().to(index))
})
.bind("127.0.0.1:8088")?
.run()
.await
}
可以在应用程序中注册任意数量的状态类型。
HttpServer
接受应用程序的factory(工厂函数)而不是应用程序实例。
Http服务器为每个线程构造一个应用程序实例,因此必须多次构造应用程序数据。
如果要在不同线程之间共享数据,则应使用可共享对象,例如
Send + Sync
。
在内部,web::Data
使用Arc
。
因此,为了避免double Arc,我们应该在使用App::app_data()
注册数据之前创建数据。
在下面的示例中,我们将编写一个具有可变共享状态的应用程序。 首先,我们定义状态并创建处理程序:
struct AppStateWithCounter {
counter: Mutex<i32>, // <- Mutex is necessary to mutate safely across threads
}
async fn _index(data: web::Data<AppStateWithCounter>) -> String {
let mut counter = data.counter.lock().unwrap(); // <- get counter's MutexGuard
*counter += 1; // <- access counter inside MutexGuard
format!("Request number: {}", counter) // <- response with count
}
并在应用程序中注册数据:
#[actix_rt::main]
async fn _main() -> std::io::Result<()> {
let counter = web::Data::new(AppStateWithCounter {
counter: Mutex::new(0),
});
HttpServer::new(move || {
// move counter into the closure
App::new()
.app_data(counter.clone()) // <- register the created data
.route("/", web::get().to(_index))
})
.bind("127.0.0.1:8088")?
.run()
.await
}
web::scope()
方法允许设置特定的应用程序前缀。
此scope表示资源前缀,该前缀将附加到资源配置添加的所有资源patterns(模式)中。
这有利于路由的配置。
例如:
#[actix_rt::main]
async fn main() {
App::new()
.service(
web::scope("/users")
.route("/show", web::get().to(show_users)));
}
在上面的示例中,*show_users路由的有效路由模式为/users/show,而不是/show,因为应用程序的scope参数将添加到该模式之前。
然后,仅当URL路径为/users/show*时,路由才会匹配,并且使用路由名称show_users调用HttpRequest.url_for()
函数时,它将生成具有相同路径的URL。
您可以将guard视为可以接受*request*对象引用并返回true 或 *false*的简单函数。
形式上,guard是实现Guard
trait的任何对象。
Actix-web
提供了几种guards,您可以检查api文档的functions section。
Header
提供了一种guards,它可以根据请求的header作为应用程序的过滤器。
#[actix_rt::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.service(
web::scope("/")
.guard(guard::Header("Host", "www.rust-lang.org"))
.route("", web::to(|| HttpResponse::Ok().body("www"))),
)
.service(
web::scope("/")
.guard(guard::Header("Host", "users.rust-lang.org"))
.route("", web::to(|| HttpResponse::Ok().body("user"))),
)
.route("/", web::to(|| HttpResponse::Ok()))
})
.bind("127.0.0.1:8088")?
.run()
.await
}
为了简单和可重用,App
和 web::Scope
都提供了configure
方法。
该函数对于将配置的部分移动到其他模块甚至库中很有用。
例如,某些resource的配置可以移至其他模块。
use actix_web::{web, App, HttpResponse, HttpServer};
// this function could be located in different module
fn scoped_config(cfg: &mut web::ServiceConfig) {
cfg.service(
web::resource("/test")
.route(web::get().to(|| HttpResponse::Ok().body("test")))
.route(web::head().to(|| HttpResponse::MethodNotAllowed())),
);
}
// this function could be located in different module
fn config(cfg: &mut web::ServiceConfig) {
cfg.service(
web::resource("/app")
.route(web::get().to(|| HttpResponse::Ok().body("app")))
.route(web::head().to(|| HttpResponse::MethodNotAllowed())),
);
}
#[actix_rt::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.configure(config)
.service(web::scope("/api").configure(scoped_config))
.route("/", web::get().to(|| HttpResponse::Ok().body("/")))
})
.bind("127.0.0.1:8088")?
.run()
.await
}
上面示例的结果将是:
/ -> "/"
/app -> "app"
/api/test -> "test"
每个ServiceConfig
可以拥有自己的data
, routes
, 和 services
。