Rust Web 全栈开发(八):添加功能并重构

Rust Web 全栈开发(八):添加功能并重构

  • [Rust Web 全栈开发(八):添加功能并重构](#Rust Web 全栈开发(八):添加功能并重构)

Rust Web 全栈开发(八):添加功能并重构

参考视频:https://www.bilibili.com/video/BV1RP4y1G7KF

继续之前的 Actix 项目,项目现状如下所示:

项目重构

在 webservice/src 目录下新建一个 models 目录,把 models.rs 移动到该目录下,重命名为 course.rs。在 models 目录下新建一个 mod.rs,添加代码:

rust 复制代码
pub mod course;

在 webservice/src 目录下新建一个 dbaccess 目录,把 db_access.rs 移动到该目录下,重命名为 course.rs。在 dbaccess 目录下新建一个 mod.rs,添加代码:

rust 复制代码
pub mod course;

在 webservice/src 目录下新建一个 handlers 目录,在 handlers 目录下新建 3 个文件:course.rsgeneral.rsmod.rs

handlers/course.rs:

rust 复制代码
use crate::state::AppState;
use crate::dbaccess::course::*;
use crate::errors::MyError;
use crate::models::course::Course;
use actix_web::{web, HttpResponse};

pub async fn new_course(
    new_course: web::Json<Course>,
    app_state: web::Data<AppState>,
) -> Result<HttpResponse, MyError> {
    post_new_course_db(&app_state.db, new_course.into())
        .await
        .map(|course| HttpResponse::Ok().json(course))
}

pub async fn get_courses_for_teacher(
    app_state: web::Data<AppState>,
    params: web::Path<i32>,
) -> Result<HttpResponse, MyError> {
    let teacher_id = params.into_inner();
    get_courses_for_teacher_db(&app_state.db, teacher_id)
        .await
        .map(|courses| HttpResponse::Ok().json(courses))
}

pub async fn get_course_detail(
    app_state: web::Data<AppState>,
    params: web::Path<(i32, i32)>,
) -> Result<HttpResponse, MyError> {
    let (teacher_id, course_id) = params.into_inner();
    get_course_details_db(&app_state.db, teacher_id, course_id)
        .await
        .map(|course| HttpResponse::Ok().json(course))
}

#[cfg(test)]
mod tests {
    use super::*;
    use actix_web::http::StatusCode;
    use std::sync::Mutex;
    use dotenv::dotenv;
    use sqlx::mysql::MySqlPoolOptions;
    use std::env;
    use chrono::{NaiveDate, NaiveDateTime, NaiveTime};

    #[actix_rt::test]
    async fn post_course_test() {
        // 检测并读取 .env 文件中的内容,若不存在也会跳过异常
        dotenv().ok();

        let db_url = env::var("DATABASE_URL")
            .expect("DATABASE_URL 没有在 .env 文件里设置");

        // 创建数据库连接池
        let db_pool = MySqlPoolOptions::new()
            .connect(&db_url)
            .await
            .unwrap();

        let course = web::Json(Course {
            teacher_id: 1,
            name: "Test course".into(),
            id: Some(3),
            time: Some(NaiveDateTime::new(
                NaiveDate::from_ymd_opt(2025, 7, 12).expect("Unknown date"),
                NaiveTime::from_hms_opt(10, 15, 0).expect("Unknown time"),
            )),
        });

        let app_state: web::Data<AppState> = web::Data::new(AppState {
            health_check_response: "".to_string(),
            visit_count: Mutex::new(0),
            db: db_pool,
        });

        // 模拟添加课程的请求
        let response = new_course(course, app_state).await.unwrap();

        assert_eq!(response.status(), StatusCode::OK);
    }

    #[actix_rt::test]
    async fn get_all_courses_success() {
        // 检测并读取 .env 文件中的内容,若不存在也会跳过异常
        dotenv().ok();

        let db_url = env::var("DATABASE_URL")
            .expect("DATABASE_URL 没有在 .env 文件里设置");

        // 创建数据库连接池
        let db_pool = MySqlPoolOptions::new()
            .connect(&db_url)
            .await
            .unwrap();

        let app_state: web::Data<AppState> = web::Data::new(AppState {
            health_check_response: "".to_string(),
            visit_count: Mutex::new(0),
            db: db_pool,
        });

        let teacher_id: web::Path<i32> = web::Path::from(1);
        let response = get_courses_for_teacher(app_state, teacher_id).await.unwrap();

        assert_eq!(response.status(), StatusCode::OK);
    }

    #[actix_rt::test]
    async fn get_one_course_success() {
        // 检测并读取 .env 文件中的内容,若不存在也会跳过异常
        dotenv().ok();

        let db_url = env::var("DATABASE_URL")
            .expect("DATABASE_URL 没有在 .env 文件里设置");

        // 创建数据库连接池
        let db_pool = MySqlPoolOptions::new()
            .connect(&db_url)
            .await
            .unwrap();

        let app_state: web::Data<AppState> = web::Data::new(AppState {
            health_check_response: "".to_string(),
            visit_count: Mutex::new(0),
            db: db_pool,
        });

        let params: web::Path<(i32, i32)> = web::Path::from((1, 1));
        let response = get_course_detail(app_state, params).await.unwrap();

        assert_eq!(response.status(), StatusCode::OK);
    }
}

handlers/general.rs:

rust 复制代码
use crate::state::AppState;
use actix_web::{web, HttpResponse};

pub async fn health_check_handler(app_state: web::Data<AppState>) -> HttpResponse {
    println!("incoming for health check");

    let health_check_response = &app_state.health_check_response;
    let mut visit_count = app_state.visit_count.lock().unwrap();
    let response = format!("{} {} times", health_check_response, visit_count);
    *visit_count += 1;

    HttpResponse::Ok().json(&response)
}

handlers/mod.rs:

rust 复制代码
pub mod course;
pub mod handlers;

对应修改 teacher_service.rs 中上述 3 个模块的路径定义:

rust 复制代码
#[path = "../dbaccess/mod.rs"]
mod dbaccess;
#[path = "../handlers/mod.rs"]
mod handlers;
#[path = "../models/mod.rs"]
mod models;

其他修改:

  1. 把 .env 文件提取到 webservice 目录之外
  2. 将各个文件中的引用从相对路径改为绝对路径

重构后的 Actix 项目结构:

相关推荐
维维酱10 小时前
四、VGA 文本缓冲区基础
rust
寻月隐君13 小时前
Rust 实战:从零构建一个多线程 Web 服务器
后端·rust·github
受之以蒙17 小时前
Rust & WebAssembly 性能调优指南:从毫秒级加速到KB级瘦身
笔记·rust·webassembly
Vallelonga1 天前
关于 Rust 异步(无栈协程)的相关疑问
开发语言·后端·rust
q567315231 天前
Rust爬虫与代理池技术解析
开发语言·爬虫·python·rust
RustFS1 天前
如何用 Trae + RustFS MCP 实现自然语言对对象存储的操作?
rust·trae
浪费笔墨1 天前
基于rust的RGBA颜色混合
rust
咸甜适中2 天前
Rust语言序列化和反序列化vec<u8>,serde库Serialize, Deserialize,bincode库(2025年最新解决方案详细使用)
开发语言·后端·rust
受之以蒙2 天前
web-sys进阶:事件处理、异步操作与 Web API 实践
笔记·rust·webassembly
寻月隐君2 天前
Rust NFT 开发实战:构建生产级的 Pinata IPFS 自动化上传工具
后端·rust·github