Actix-Web路由

一、路由

上一篇文章,介绍了Actix-Web入门知识,链接如下:https://www.cnblogs.com/xiao987334176/p/19271995

接下来介绍路由

基础路由

修改主代码 src/main.rs

use actix_web::{get, post, put, delete, web, HttpResponse};
use serde::{Deserialize, Serialize};
 
#[derive(Serialize, Deserialize)]
struct User {
    id: u32,
    name: String,
    email: String,
}
 
// GET 请求
#[get("/users")]
async fn get_users() -> HttpResponse {
    let users = vec![
        User {
            id: 1,
            name: "Alice".to_string(),
            email: "alice@example.com".to_string(),
        },
        User {
            id: 2,
            name: "Bob".to_string(),
            email: "bob@example.com".to_string(),
        },
    ];
    
    HttpResponse::Ok().json(users)
}
 
// GET 请求(带路径参数)
#[get("/users/{id}")]
async fn get_user(path: web::Path<u32>) -> HttpResponse {
    let user_id = path.into_inner();
    
    let user = User {
        id: user_id,
        name: "Alice".to_string(),
        email: "alice@example.com".to_string(),
    };
    
    HttpResponse::Ok().json(user)
}
 
// POST 请求(创建资源)
#[post("/users")]
async fn create_user(user: web::Json<User>) -> HttpResponse {
    println!("创建用户: {:?}", user.name);
    HttpResponse::Created().json(user.into_inner())
}
 
// PUT 请求(更新资源)
#[put("/users/{id}")]
async fn update_user(
    path: web::Path<u32>,
    user: web::Json<User>,
) -> HttpResponse {
    let user_id = path.into_inner();
    println!("更新用户 {}: {:?}", user_id, user.name);
    HttpResponse::Ok().json(user.into_inner())
}
 
// DELETE 请求
#[delete("/users/{id}")]
async fn delete_user(path: web::Path<u32>) -> HttpResponse {
    let user_id = path.into_inner();
    println!("删除用户: {}", user_id);
    HttpResponse::NoContent().finish()
}

以上是示例代码,看不出效果。接下来,将以上接口注册到Swagger

use actix_web::{delete, get, post, put, web, App, HttpResponse, HttpServer, Responder};
use serde::{Deserialize, Serialize};
use utoipa::{OpenApi, ToSchema};
use utoipa_swagger_ui::SwaggerUi;

#[derive(Serialize, Deserialize, ToSchema, Clone, Debug)]
struct User {
    id: u32,
    name: String,
    email: String,
}

/* ---------- hello ---------- */
#[derive(Serialize, ToSchema)]
struct HelloReply {
    message: String,
}

/// 根路径接口
#[utoipa::path(
    get,
    path = "/",
    responses((status = 200, description = "Say hello", body = HelloReply)),
    tag = "hello"
)]
#[get("/")]
async fn hello() -> impl Responder {
    web::Json(HelloReply {
        message: "Hello, world!".to_owned(),
    })
}

/* ---------- user ---------- */
/// 列表用户
#[utoipa::path(
    get,
    path = "/users",
    responses((status = 200, description = "用户列表", body = Vec<User>)),
    tag = "user"
)]
#[get("/users")]
async fn get_users() -> HttpResponse {
    HttpResponse::Ok().json(vec![
        User { id: 1, name: "Alice".into(), email: "alice@example.com".into() },
        User { id: 2, name: "Bob".into(),   email: "bob@example.com".into() },
    ])
}

/// 根据 ID 查用户
#[utoipa::path(
    get,
    path = "/users/{id}",
    params(("id" = u32, Path, description = "用户主键")),
    responses((status = 200, description = "单个用户", body = User)),
    tag = "user"
)]
#[get("/users/{id}")]
async fn get_user(id: web::Path<u32>) -> HttpResponse {
    HttpResponse::Ok().json(User {
        id: *id,
        name: "Alice".into(),
        email: "alice@example.com".into(),
    })
}

/// 新建用户
#[utoipa::path(
    post,
    path = "/users",
    request_body = User,
    responses((status = 201, description = "创建成功", body = User)),
    tag = "user"
)]
#[post("/users")]
async fn create_user(user: web::Json<User>) -> HttpResponse {
    HttpResponse::Created().json(user.into_inner())
}

/// 更新用户
#[utoipa::path(
    put,
    path = "/users/{id}",
    params(("id" = u32, Path, description = "用户主键")),
    request_body = User,
    responses((status = 200, description = "更新成功", body = User)),
    tag = "user"
)]
#[put("/users/{id}")]
async fn update_user(id: web::Path<u32>, user: web::Json<User>) -> HttpResponse {
    let _ = id.into_inner();
    HttpResponse::Ok().json(user.into_inner())
}

/// 删除用户
#[utoipa::path(
    delete,
    path = "/users/{id}",
    params(("id" = u32, Path, description = "用户主键")),
    responses((status = 204, description = "删除成功")),
    tag = "user"
)]
#[delete("/users/{id}")]
async fn delete_user(id: web::Path<u32>) -> HttpResponse {
    let _ = id.into_inner();
    HttpResponse::NoContent().finish()
}

/// OpenApi 文档
#[derive(OpenApi)]
#[openapi(
    paths(hello, get_users, get_user, create_user, update_user, delete_user),
    components(schemas(HelloReply, User)),
    tags(
        (name = "hello", description = "hello接口"),
        (name = "user",    description = "用户管理接口")
    )
)]
struct ApiDoc;

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    env_logger::init_from_env(env_logger::Env::default().default_filter_or("info"));
    log::info!("Starting HTTP server on http://127.0.0.1:8080");

    HttpServer::new(|| {
        App::new()
            .service(hello)
            .service(get_users)
            .service(get_user)
            .service(create_user)
            .service(update_user)
            .service(delete_user)
            .service(
                SwaggerUi::new("/swagger-ui/{_:.*}")
                    .url("/api-doc/openapi.json", ApiDoc::openapi()),
            )
    })
    .bind(("127.0.0.1", 8080))?
    .run()
    .await
}

重新运行,访问Swagger UI:http://localhost:8080/swagger-ui/

image

这样就比较清晰了,接口做了分组显示。

先来测试列表用户,点击“Try it out”调试,可以看到返回了2条用户信息

image

查询参数与表单处理

 修改主代码 src/main.rs

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

 
#[derive(Deserialize)]
struct QueryParams {
    page: Option<u32>,
    limit: Option<u32>,
    search: Option<String>,
}
 
// 查询参数
#[get("/search")]
async fn search(query: web::Query<QueryParams>) -> HttpResponse {
    let page = query.page.unwrap_or(1);
    let limit = query.limit.unwrap_or(10);
    let search_term = query.search.as_deref().unwrap_or("");
    
    HttpResponse::Ok().json(serde_json::json!({
        "page": page,
        "limit": limit,
        "search": search_term,
        "results": []
    }))
}
 
// 表单数据
#[derive(Deserialize)]
struct LoginForm {
    username: String,
    password: String,
}
 
#[post("/login")]
async fn login(form: web::Json<LoginForm>) -> HttpResponse {
    println!("登录用户: {}", form.username);
    
    // 模拟验证
    if form.username == "admin" && form.password == "password" {
        HttpResponse::Ok().json(serde_json::json!({
            "status": "success",
            "token": "mock-jwt-token"
        }))
    } else {
        HttpResponse::Unauthorized().json(serde_json::json!({
            "status": "error",
            "message": "Invalid credentials"
        }))
    }
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    env_logger::init_from_env(env_logger::Env::default().default_filter_or("info"));
    log::info!("Starting HTTP server on http://127.0.0.1:8080");

    HttpServer::new(|| {
        App::new()
            .service(search)
            .service(login)
    })
    .bind(("127.0.0.1", 8080))?
    .run()
    .await
}

重新运行,访问:http://127.0.0.1:8080/search

效果如下:

image

使用postman调用接口,http://127.0.0.1:8080/login

指定请求头类型为json

image

 输出json参数

{
    "username":"admin",
    "password":"password"
}

请求接口,就可以得到json返回信息,效果如下

image

路由组织(Scope)

use actix_web::{web,get,post, HttpResponse,HttpServer,App};
use serde::Deserialize;
use actix_web::web::Path; 

 
#[derive(Deserialize)]
struct QueryParams {
    page: Option<u32>,
    limit: Option<u32>,
    search: Option<String>,
}
 
// 查询参数
#[get("/search")]
async fn search(query: web::Query<QueryParams>) -> HttpResponse {
    let page = query.page.unwrap_or(1);
    let limit = query.limit.unwrap_or(10);
    let search_term = query.search.as_deref().unwrap_or("");
    
    HttpResponse::Ok().json(serde_json::json!({
        "page": page,
        "limit": limit,
        "search": search_term,
        "results": []
    }))
}
 
// 表单数据
#[derive(Deserialize)]
struct LoginForm {
    username: String,
    password: String,
}
 
#[post("/login")]
async fn login(form: web::Json<LoginForm>) -> HttpResponse {
    println!("登录用户: {}", form.username);
    
    // 模拟验证
    if form.username == "admin" && form.password == "password" {
        HttpResponse::Ok().json(serde_json::json!({
            "status": "success",
            "token": "mock-jwt-token"
        }))
    } else {
        HttpResponse::Unauthorized().json(serde_json::json!({
            "status": "error",
            "message": "Invalid credentials"
        }))
    }
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    env_logger::init_from_env(env_logger::Env::default().default_filter_or("info"));
    log::info!("Starting HTTP server on http://127.0.0.1:8080");

    HttpServer::new(|| {
        App::new()
            // API v1 路由组
            .service(
                web::scope("/api/v1")
                    .service(
                        web::scope("/users")
                            .service(search)
                    )
                    .service(
                        web::scope("/posts")
                            .route("", web::get().to(get_posts))
                            .route("/{id}", web::get().to(get_post))
                    )
            )
            // API v2 路由组
            .service(
                web::scope("/api/v2")
                    .service(search)
            )
    })
    .bind(("127.0.0.1", 8080))?
    .run()
    .await
}

async fn get_posts() -> HttpResponse {
    HttpResponse::Ok().json(Vec::<String>::new())
}
 

async fn get_post(id: Path<PostId>) -> HttpResponse {
    println!("收到 post id: {}", id.id);          // <-- 这里打印id
    HttpResponse::Ok().json(serde_json::json!({
        "post_id": id.id
    }))
}

// 路径参数结构体
#[derive(serde::Deserialize)]
struct PostId {
    id: String,   // 如果想用 u32 也行,这里先用 String 更宽松
}

路由结构可视化:

/api/v1
  ├── /users
  │   ├── GET    /          (获取用户列表)
  │   ├── GET    /{id}      (获取单个用户)
  │   ├── POST   /          (创建用户)
  │   ├── PUT    /{id}      (更新用户)
  │   └── DELETE /{id}      (删除用户)
  └── /posts
      ├── GET    /          (获取文章列表)
      └── GET    /{id}      (获取单篇文章)

以上代码,可以访问以下几个接口

api v1

http://127.0.0.1:8080/api/v1/users/search

image

 http://127.0.0.1:8080/api/v1/posts

输出:[]

http://127.0.0.1:8080/api/v1/posts/123

这里会打印出id参数

image

api v2

http://127.0.0.1:8080/api/v2/search

image

本文参考链接:https://blog.youkuaiyun.com/sinat_41617212/article/details/154069236

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值