【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 分钟前
springcloud alibaba(九)Nacos Config服务配置
后端·spring·spring cloud
Dargon28822 分钟前
实例讲解Simulink的MATLAB Function模块
开发语言·matlab·simulink·mbd软件开发
Victor35623 分钟前
Netty(26)如何实现基于Netty的RPC框架?
后端
charlie11451419123 分钟前
现代C++嵌入式教程:C++98基础特性:从C到C++的演进(1)
c语言·开发语言·c++·笔记·学习·教程
Victor35633 分钟前
Netty(25)Netty的序列化和反序列化机制是什么?
后端
历程里程碑1 小时前
C++ 18智能指针:告别内存泄漏的利器
开发语言·c++
qq_12498707531 小时前
重庆三峡学院图书资料管理系统设计与实现(源码+论文+部署+安装)
java·spring boot·后端·mysql·spring·毕业设计
桦说编程1 小时前
并发编程高级技巧:运行时检测死锁,告别死锁焦虑
java·后端·性能优化
刘某的Cloud2 小时前
列表、元组、字典、集合-组合数据类型
linux·开发语言·python
梁同学与Android2 小时前
Android ---【经验篇】ArrayList vs CopyOnWriteArrayList 核心区别,怎么选择?
android·java·开发语言