Rust三方库-actix

Actix

Actix 是一个Rust异步Webserver,它提供了构建webserver需要的多种能力,包括路由、中间件、请求预处理、相应后处理等等。所有的actix server均围绕App构建,用于为资源和中间件注册路由。在相同的scope里面的所有handler存储程序状态。scope类似于所有路由的namespace。应用程序总是以/开始,如果没有这个prefix系统将自动加入。这个prefix将由路径段组成。例如:/app开头的scope,任何/app或者/app/test都将匹配路由。但是/application将不会匹配。

use actix_web::{web, App, HttpServer, Responder};
async fn index() -> impl Responder {
    "hello world"
}
#[actix_web::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", 8888))?
    .run()
    .await
}

上面的代码我们声明了一个scope叫做app,然后路由index.html,路由相应函数为index。我们可以使用:curl -X GET http://127.0.0.1:8888/app/index.html访问并获取index()函数输出hello world

状态

在一个scope下应用程序状态在所有路由和资源之间共享,状态可以通过web::Data<T>访问,这里的T是状态的类型。状态对中间件也是可以访问的。

use actix_web::{get, web, App, HttpServer};
struct AppState {
    app_name: String,
}
#[get("/")]
async fn index(data: web::Data<AppState>) -> String {
    let app_name = &data.app_name;
    format!("Hello {app_name}")
}
#[actix_web::main]
async fn main() -> 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", 8888))?
    .run()
    .await
}

在上面的代码中我们定义了一个结构体AppState用来存储用户的APP名称信息。HttpServer启动的时候创建了一个实例,实例通过web::Data::new()构造。然后service实现对/路由的绑定index,index会对拿到的web::Data结构体输出内容格式化返回输出。我们可以curl -X GET http://127.0.0.1:8888获取/路由的输出Hello Actix web

可修改的共享状态

HttpServer更愿意接受一个应用程序工厂。一个HttpServer为每个线程构建一个应用。因此应用程序的数据可能被多次构建,不同的线程间共享对象需要实现Send+Sync trait。web::Data内部使用Arc,为了避免创建两个Arc。我们应该在使用App::app_data之前注册它。

::{get, web, App, HttpServer};
use std::sync::Mutex;
struct AppStateWithCounter {
    counter: Mutex<i32>, //线程锁
}
async fn index(data: web::Data<AppStateWithCounter>) -> String {
    let mut counter = data.counter.lock().unwrap();
    *counter += 1;
    format!("请求次数: {counter}")
}
#[actix_web::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", 8888))?
    .run()
    .await
}

上面的代码中:

  1. 状态在闭包中传给HttpServer::new的本地工作线程如果被修改则会同步。
  2. 为了获得全局的共享状态,它必须在闭包外部创建然后传入HttpServer::new然后move或者clone它。

使用Application Scope整合应用

web::scope()方法允许设置资源组prefix。代表它将被加入资源配置的前面,通过它可以挂在一些不同位置的资源,同时保证资源的名称好访问。

应用guard和virtual hosing

你可以认为guard是一个接受request对象返回true或者false的简单函数。一个guard实现了Guard trait。Actix web提供了一些guard,一个常见的guards是Host。他可以用作请求头信息的filter。

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

configure

简化和重用App和web::Scope提供了配置方法。这个函数用于移动配置的一部分到不同的module或者库。


请求handler

请求handler是一个异步函数,从请求中接收0或者更多参数返回一个可以被转换为HttpResponse类型。请求handling发生在两个阶段,首先handler对象调用返回任意实现了Respinder的trait,然后respond_to()调用转换其为HttpResponse或者Error。
默认Actix web提供了对一些标准类型的Responder实现。比如&'static str'、String等等。可用handder的例子:

async fn index(_req: HttpRequest) -> &'static str {
    "Hello world!"
}

async fn index(_req: HttpRequest) -> String {
    "Hello world!".to_owned()
}
async fn index(_req: HttpRequest) -> impl Responder {
    web::Bytes::from_static(b"Hello world!")
}
async fn index(req: HttpRequest) -> Box<Future<Item=HttpResponse, Error=Error>> {
    ...
}

自定义类型的相应

为了直接从handler函数返回自定义类型,类型需要实现Responder trait。创建一个自订立类型序列化为application/json的相应:

use actix_web::{
    body::BoxBody, http::header::ContentType, HttpRequest, HttpResponse, Responder,
};
use serde::Serialize;

#[derive(Serialize)]
struct MyObj {
    name: &'static str,
}

// Responder
impl Responder for MyObj {
    type Body = BoxBody;

    fn respond_to(self, _req: &HttpRequest) -> HttpResponse<Self::Body> {
        let body = serde_json::to_string(&self).unwrap();

        // Create response and set content type
        HttpResponse::Ok()
            .content_type(ContentType::json())
            .body(body)
    }
}

async fn index() -> impl Responder {
    MyObj { name: "user" }
}

响应体流

Response body 可以异步生成,在这个case下,body必须实现stream trait Stream<Item=Bytes,Error=Error>:

use actix_web::{get, web, App, Error, HttpResponse, HttpServer};
use futures::{future::ok, stream::once};

#[get("/stream")]
async fn stream() -> HttpResponse {
    let body = once(ok::<_, Error>(web::Bytes::from_static(b"test")));

    HttpResponse::Ok()
        .content_type("application/json")
        .streaming(body)
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| App::new().service(stream))
        .bind(("127.0.0.1", 8080))?
        .run()
        .await
}

不同的返回类型

有时候你需要返回不同类型的response。例如,错误检查和错误返回,返回异步相应或者任何要求两个不同类型结果的情况。再这种情况下Either类型可以使用,Either允许结合不同类型的responder类型为单个类型。

use actix_web::{Either, Error, HttpResponse};

type RegisterResult = Either<HttpResponse, Result<&'static str, Error>>;

async fn index() -> RegisterResult {
    if is_a_variant() {
        // choose Left variant
        Either::Left(HttpResponse::BadRequest().body("Bad data"))
    } else {
        // choose Right variant
        Either::Right(Ok("Hello!"))
    }
}

   转载规则


《Rust三方库-actix》 bleedingfight 采用 知识共享署名 4.0 国际许可协议 进行许可。
 上一篇
Rust基础(Option) Rust基础(Option)
Option 表示值,如果有值则Some包装其值,否则返回None。 fn divide(a: f32, b: f32) -> Option<f32> { if b == 0.0f32 { None }
下一篇 
Rust基础(字符串) Rust基础(字符串)
字符串rust String类似于C++中的std::string,本质上是一种容纳字符串的容器。但是多了更多的操作方法。字符串字面量是Rust中的常规字符串,它本身也有一些方法。 字符串切片字符串字面量常见的方法: len:获取字符串元
  目录