应用

写一个应用

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部分。

State 状态

应用程序状态与同一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
}

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

Shared Mutable State 共享可变状态

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
}

使用应用程序的Scope来编写应用程序

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。

应用程序guards(守卫)和虚拟主机

您可以将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
}

Configure 配置

为了简单和可重用,Appweb::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

接下来: Server 服务器