【Rust Crate之Actix Web(一)】

Rust Crate之Actix Web

  • [什么是Actix Web?](#什么是Actix Web?)
  • [Actix Web 入门](#Actix Web 入门)
    • [代码宏展开,看看` #[get("/")] ` 做了什么](#[get("/")] ` 做了什么)
    • [Actix Web中的State](#Actix Web中的State)
    • [Actix Web中的scope](#Actix Web中的scope)
    • [Actix Web中的extractors](#Actix Web中的extractors)
  • 总结

什么是Actix Web?

Actix Web is a poweful ,pragmatic,and extremely fast web framework for Rust

Actix Web 作为一个服务器框架,非常适合于搭建小型http服务器,方便快捷,它支持Http/1,Http2,TLS,尽管他也支持了Websocket,但在此不予讨论。

Actix Web 入门

代码示例:

rust 复制代码
use actix_web::{get, post, web, App, HttpResponse, HttpServer, Responder};

#[get("/")]           //使用ACtix定义的宏,可以直接将响应函数定义具体路由地址(url),并确定以什么方式访问(如这里的根,Get方式),
async fn hello() -> impl Responder {
    HttpResponse::Ok().body("Hello world!")
}

#[post("/echo")]
async fn echo(req_body: String) -> impl Responder {
    HttpResponse::Ok().body(req_body)
}

async fn manual_hello() -> impl Responder {
    HttpResponse::Ok().body("Hey there!")
}

#[actix_web::main]  //标明Actix web程序入口,默认支持异步编程
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| { //创建HttpServer
        App::new()       //使用App 实例注册request处理程序.
            .service(hello) //使用宏路由的,用sercvice注册
            .service(echo)  
            .route("/hey", web::get().to(manual_hello)) //也可以手动,更清晰的注册路由
    })
    .bind(("127.0.0.1", 8080))?//绑定具体的Ip & Port
    .run()            //run起来您的程序
    .await
}
//ps:以上代码为官方示例

代码宏展开,看看#[get("/")] 做了什么

rust 复制代码
#![feature(prelude_import)]
#[prelude_import]
use std::prelude::rust_2021::*;
#[macro_use]
extern crate std;
use actix_web::{get, post, web, App, HttpResponse, HttpServer, Responder};
#[allow(non_camel_case_types, missing_docs)]
pub struct hello;
// 正如官文中描述的那样:Finally, the app is started inside an HttpServer which will serve incoming 
// requests using your App as an "application factory". 每一个request处理程序都实现了HttpServiceFactory
impl ::actix_web::dev::HttpServiceFactory for hello {
    fn register(self, __config: &mut actix_web::dev::AppService) {
    //register将会在Service做为参数传入时,最终调用HttpServiceFactory::register将resource注册
        async fn hello() -> impl Responder {
            HttpResponse::Ok().body("Hello world!")
        }
        let __resource = ::actix_web::Resource::new("/")
            .name("hello")
            .guard(::actix_web::guard::Get())
            .to(hello);
        ::actix_web::dev::HttpServiceFactory::register(__resource, __config);
    }
}
#[allow(non_camel_case_types, missing_docs)]
pub struct echo;
impl ::actix_web::dev::HttpServiceFactory for echo {
    fn register(self, __config: &mut actix_web::dev::AppService) {
        async fn echo(req_body: String) -> impl Responder {
            HttpResponse::Ok().body(req_body)
        }
        let __resource = ::actix_web::Resource::new("/echo")
            .name("echo")
            .guard(::actix_web::guard::Post())
            .to(echo);
        ::actix_web::dev::HttpServiceFactory::register(__resource, __config);
    }
}
async fn manual_hello() -> impl Responder {
    HttpResponse::Ok().body("Hey there!")
}
fn main() -> std::io::Result<()> {
    <::actix_web::rt::System>::new()
        .block_on(async move {
            {
                HttpServer::new(|| {
                        App::new()
                            .service(hello)
                            .service(echo)
                            .route("/hey", web::get().to(manual_hello))
                    })
                    .bind(("127.0.0.1", 8080))?
                    .run()
                    .await
            }
        })
}

Actix Web中的State

Actix web 框架下的State,即承载Server内部非请求数据的数据载体,不要被名称迷惑,它可以通过自定义数据来表示Server的状态,而更多的是用以共享全局性质的变量,通常来讲,如在开发时对数据库链接管理的数据库连接池,也可以是只读的base_url,在共享此类变量时需要注意,Actix Web Server针对每一条数据请求都是会独立出一条thread,所以共享时的同步尤为重要,所以在实现自定义的数据时,请注意使用同步原语。

代码示例:

rust 复制代码
...
let db_pool = Data::new(db_pool);
....app_data(db_pool.clone())...

State在闭包直接初始化时是无法做同步的,当开发者需要同步机制,则需要如上述代码一般,先在外部声明,再clone传入。

Actix Web中的scope

Scope表示范围,在服务中即表示统一前缀,举个例子:/user/login /user/info中的/user便是前缀,这方便开发者将api以restful形式拆分,更好的做分类,进一步的,开发者可以通过ServiceConfig 将具体的请求处理程序分配到不同的module中,使得整个程序代码更加的结构化。

rust 复制代码
use actix_web::{web, App, HttpResponse, HttpServer};

// this function could be located in a different module
fn scoped_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)),
    );
}

// this function could be located in a different module
fn config(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]
async fn main() -> 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
}
//ps: 例子来源于官文

Actix Web中的extractors

extractors在Acitx Web中是类型安全的,extractors提供了提取请求数据中为特定类型的不同方法,使得代码更加清晰安全.

Path

Path用于将请求路径上的信息转为自定义类型。

rust 复制代码
//具体的
#[derive(Deserialize)] //自定义类型一定需要实现反序列化
struct Info {
    user_id: u32,
    friend: String,
}
/// extract path info using serde
#[get("/users/{user_id}/{friend}")] //宏中名称要和自定义类型中的名称对齐.
async fn index(info: web::Path<Info>) -> Result<String> {
    Ok(format!(
        "Welcome {}, user_id {}!",
        info.friend, info.user_id
    ))
}

Query

Query用于请求参数的提取,将其转换成具体的类型。

rust 复制代码
#[derive(Deserialize)]
struct Info {
    username: String,
}

//如果请求参数中并不包含需要提取的字段数据,则server将会返回404
#[get("/")]
async fn index(info: web::Query<Info>) -> String {
    format!("Welcome {}!", info.username)
}

JSON

JSON 用于将请求中的json格式请求体转为具体的类型。

rust 复制代码
use actix_web::{post, web, App, HttpServer, Result};
use serde::Deserialize;

#[derive(Deserialize)]
struct Info {
    username: String,
}

/// deserialize `Info` from request's body
#[post("/submit")]
async fn submit(info: web::Json<Info>) -> Result<String> {
    Ok(format!("Welcome {}!", info.username))
}

URL-encoded form

用于将表单数据提取成具体的数据类型。

rust 复制代码
use actix_web::{post, web, App, HttpServer, Result};
use serde::Deserialize;

#[derive(Deserialize)]
struct FormData {
    username: String,
}

/// extract form data using serde
/// this handler gets called only if the content type is *x-www-form-urlencoded*
/// and the content of the request could be deserialized to a `FormData` struct
#[post("/")]
async fn index(form: web::Form<FormData>) -> Result<String> {
    Ok(format!("Welcome {}!", form.username))
}

涉及到更具体的可以参阅官方文档的Api Document

Actix Web架构摘要


总结

Actix Web 易用性高,性能出众,如果开发者想在生产环境中使用Rust做Http Server,其是值得一选的,在整体的开发框架中,State同步,提取和Connfigure 以及Handler的 布局设计是重要的。

"不论我们接受与否,一个不确定的时代已经到来"

相关推荐
沐知全栈开发2 分钟前
Bootstrap5 轮播
开发语言
roamingcode3 分钟前
我是如何 Vibe Coding,将 AI CLI 工具从 Node.js 迁移到 Rust 并成功发布的
人工智能·rust·node.js·github·claude·github copilot
kiss strong4 分钟前
springboot替换word模板&加入二维码&加水印&转为pdf
spring boot·后端·pdf
Stecurry_306 分钟前
Springboot整合SpringMVC --从0到1
java·spring boot·后端
༾冬瓜大侠༿7 分钟前
C++string
c语言·开发语言·c++·算法
雨季6668 分钟前
Flutter 三端应用实战:OpenHarmony “极简文本字符计数器”——量化表达的尺度
开发语言·flutter·ui·交互·dart
skywalker_1111 分钟前
多线程&JUC
java·开发语言·jvm·线程池
黎雁·泠崖12 分钟前
Java基础核心能力总结:从语法到API的完整知识体系
java·开发语言
JaguarJack17 分钟前
PHP 现在可以零成本构建原生 iOS 和 Android 应用 NativePHP for Mobile v3 发布
后端·php·laravel·服务端
雨季66619 分钟前
Flutter 三端应用实战:OpenHarmony “呼吸灯”——在焦虑时代守护每一次呼吸的数字禅修
开发语言·前端·flutter·ui·交互