探索Actix Web:高性能异步 Rust Web框架

2023年 9月 16日 90.2k 0

Actix Web 是一款基于 Rust 语言开发的高性能 Web 框架。它通过异步编程模型、强大的请求路由、中间件支持,为开发者提供了丰富的工具和选项,是构建可伸缩、高并发的 Web 应用程序的理想选择。

初始化

新建工程actix-test。

cargo new actix-test
cd actix-test

Cargo.toml增加依赖:

[dependencies]
actix-web = "4"

接口代码

修改src/main.ts:

use actix_web::{get, post, web, App, HttpResponse, HttpServer, Responder};

#[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()
        .cookie(
            Cookie::build("name", "value")
                .domain("localhost")
                .path("/")
                .secure(false)
                .http_only(true)
                .finish(),
        )
        .body("Hey there!")
}

#[actix_web::main]
async fn main() -> std::io::Result {
    HttpServer::new(|| {
        App::new()
        .service(hello)
        .service(echo)
        .route("/hey", web::get().to(manual_hello))
    })
    .bind(("127.0.0.1", 8080))?
    .run()
    .await
}

验证

使用cargo run,运行服务。

http://127.0.0.1:8080

image.png

http://127.0.0.1:8080/hey

image.png

还有个post请求:

fetch('echo', {method: 'post', body: JSON.stringify({a: 'b'}), headers: {'content-type':'application/json'}});

image.png

参数

参数获取

path

url路径中的参数中可以使用web::Path来整合成一个Struct对象:

use actix_web::{get, web, App, HttpServer, Result};
use serde::Deserialize;

#[derive(Deserialize)]
struct Info {
    user_id: u32,
    friend: String,
}

/// extract path info using serde
#[get("/users/{user_id}/{friend}")] //  Result {
    Ok(format!(
        "Welcome {}, user_id {}!",
        info.friend, info.user_id
    ))
}

这里用到了serde::Deserialize,所以需要在toml里添加依赖:

serde =  {version = "1", features =["derive"]}

要使用derive,就必须这样添加features,否则会报

cannot find derive macro Deserializ in this scope

也可以用web::Path只取某一个参数:

#[get("/hello/{name}")]
async fn greet(name: web::Path) -> impl Responder {
    format!("Hello {name}!")
}

Query

Query与path类似,可以获取/hello?name=test&age=16中的name与age。

#[derive(serde::Deserialize)]
struct MyParams {
    name: String,
    age: u16,
}

#[get("/hello")]
async fn hello(params: web::Query) -> String {
    format!("Hello, {}! You are {} years old.", params.name, params.age)
}

POST

对于POST请求,可以使用web::Json获取Body中的参数:

#[derive(serde::Deserialize)]
struct MyParams {
    name: String,
    age: u8,
}

#[post("/hello")]
async fn hello(params: web::Json) -> impl Responder {
    HttpResponse::Ok().body(format!(
        "Hello, {}! You are {} years old.",
        params.name, params.age
    ))
}

参数校验

参数校验通常发生在POST请求里,因为它的参数通常比较复杂。如果每个接口都写一遍参数校验处理,那就太Low了。可以使用validator来简化代码。

use serde::{Deserialize, Serialize};
use validator::Validate;

#[derive(Deserialize, Debug, Serialize, Validate)]
pub struct CreateJobDto {
    #[validate(length(min = 10, max = 100))]
    pub old_image: String,

    #[validate(length(min = 10, max = 100))]
    pub new_image: String,

    pub status: JobStatus,
    pub user_id: String,
}


use actix_web::error::ErrorBadRequest;
use actix_web::Error;

#[post("createJob")]
pub async fn create_job(
    db: web::Data,
    params: web::Json,
) -> Result {
    params.validate().map_err(ErrorBadRequest)?;
    match job_service::create_job(&db, params.into_inner()).await {
        Ok(id) => Ok(HttpResponse::Ok().body(id)),
        Err(err) => {
            if err.to_string().contains("E11000 duplicate key error") {
                Ok(HttpResponse::BadRequest().body("old_image重复"))
            } else {
                log::error!("error: {}", err);
                Ok(HttpResponse::InternalServerError().body(err.to_string()))
            }
        }
    }
}

Guard

config.service(
        web::scope("/api/user")
            .guard(guard::Header("x-guarded", "secret"))
            .service(auth_controller::login));

这里的Guard与NestJS中意义上不同的是,它只是保护接口,被过滤掉的接口只会响应404。

所以要做到NestJS中的接口保护,只能写中间件。下面来具体介绍如何实现。

简单Guard

这是一个不需要注入用户信息的普通Guard:

use crate::globals;
use actix_web::{
dev::{self, Service},
error::ErrorForbidden,
};
use actix_web::{
dev::{ServiceRequest, ServiceResponse, Transform},
Error,
};
use futures_util::future::LocalBoxFuture;
use std::{
future::{ready, Ready},
rc::Rc,
};

pub struct SimpleGuard;

impl Transform for SSOGuard
where
S: Service,
S::Future: 'static,
B: 'static,
{
type Response = ServiceResponse;
type Error = Error;
type InitError = ();
type Transform = SSOGuardMiddleware;
type Future = Ready;

fn new_transform(&self, service: S) -> Self::Future {
ready(Ok(SSOGuardMiddleware {
service: Rc::new(service),
}))
}
}
pub struct SSOGuardMiddleware {
// This is special: We need this to avoid lifetime issues.
service: Rc,
}

impl Service for SSOGuardMiddleware
where
S: Service + 'static,
S::Future: 'static,
B: 'static,
{
type Response = ServiceResponse;
type Error = Error;
type Future = LocalBoxFuture

相关文章

JavaScript2024新功能:Object.groupBy、正则表达式v标志
PHP trim 函数对多字节字符的使用和限制
新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
为React 19做准备:WordPress 6.6用户指南
如何删除WordPress中的所有评论

发布评论