在建構任何線上平台時,對核心資源的管理功能都是不可或缺的。接續前文的使用者註冊與登入功能,本文將深入探討如何為一個線上教學平台,設計並實現一套完整的課程管理 (CRUD - Create, Read, Update, Delete) RESTful API。我們將遵循良好的軟體工程實踐,將相關的 handler 組織到獨立的模組中,並為每個功能設計清晰的 API 端點。
步驟一:程式碼組織與模組化
隨著應用功能的增加,將所有 handler 放在單一檔案中會變得難以維護。一個良好的實踐是根據功能領域將程式碼模組化。
- 建立
handler模組: 在src/目錄下建立handler/資料夾。 - 劃分功能模組:
- 將原有的註冊、登入相關 handler 移至
src/handler/auth.rs。 - 建立一個新的
src/handler/course.rs檔案,專門用來存放課程管理的 handler。
- 將原有的註冊、登入相關 handler 移至
- 宣告模組: 在
src/handler/mod.rs中宣告這些子模組,以便外部可以存取。// src/handler/mod.rs pub mod auth; pub mod course;
步驟二:課程管理 API 路由設計
我們將所有與課程相關的 API 端點都歸納在 /courses 這個範疇 (scope) 之下,這有助於保持 API 的一致性與可讀性。
在 src/routes.rs 中,我們定義課程管理的路由設定:
// src/routes.rs
use crate::handler::course::{handle_delete_course, handle_insert_course, handle_update_course};
use actix_web::web;
pub fn course_routes(cfg: &mut web::ServiceConfig) {
cfg.service(
web::scope("/courses")
.route("/{tutor_id}", web::post().to(handle_insert_course))
.route("/{tutor_id}/{course_id}", web::put().to(handle_update_course))
.route("/{tutor_id}/{course_id}", web::delete().to(handle_delete_course))
);
}
路由設計解說:
POST /courses/{tutor_id}: 為指定的導師新增一門課程。PUT /courses/{tutor_id}/{course_id}: 更新指定導師的某一門特定課程。DELETE /courses/{tutor_id}/{course_id}: 刪除指定導師的某一門特定課程。
最後,在 src/main.rs 的 App 設定中,載入這個路由組態:
// 在 main.rs 的 HttpServer::new(...) 中
.configure(course_routes) // 載入課程路由
.configure(auth_routes) // 載入認證路由
步驟三:資料模型定義
為了處理課程資料的傳遞,我們需要定義相應的 Rust 結構體。
在 src/models.rs 中定義:
// src/models.rs
use serde::{Deserialize, Serialize};
// 用於接收新增課程請求的資料結構
#[derive(Deserialize, Debug)]
pub struct NewCourse {
pub course_name: String,
pub course_description: Option<String>,
// ... 其他課程相關欄位
}
// 用於接收更新課程請求的資料結構
#[derive(Deserialize, Debug)]
pub struct UpdateCourse {
pub course_name: Option<String>,
pub course_description: Option<String>,
// ... 其他可選的更新欄位
}
// 用於回傳給客戶端的課程資料結構
#[derive(Serialize, Debug)]
pub struct CourseResponse {
pub course_id: i32,
pub tutor_id: i32,
pub course_name: String,
// ... 其他欄位
}
步驟四:實現課程管理 Handlers
現在,我們在 src/handler/course.rs 中為每個路由編寫對應的 handler 存根 (stub)。在實際開發中,這些 handler 內部會呼叫資料庫存取層的函式來完成操作。
// src/handler/course.rs
use actix_web::{web, Error, HttpResponse};
use crate::state::AppState;
use crate::models::{NewCourse, UpdateCourse};
// POST /courses/{tutor_id}
pub async fn handle_insert_course(
_app_state: web::Data<AppState>,
_tutor_id: web::Path<i32>,
_new_course: web::Json<NewCourse>,
) -> Result<HttpResponse, Error> {
// TODO: 呼叫資料庫函式,將 _new_course 存入資料庫
println!("Received new course request");
Ok(HttpResponse::Ok().body("成功新增課程"))
}
// PUT /courses/{tutor_id}/{course_id}
pub async fn handle_update_course(
_app_state: web::Data<AppState>,
_path: web::Path<(i32, i32)>,
_update_course: web::Json<UpdateCourse>,
) -> Result<HttpResponse, Error> {
// TODO: 呼叫資料庫函式,更新對應的課程
Ok(HttpResponse::Ok().body("成功更新課程"))
}
// DELETE /courses/{tutor_id}/{course_id}
pub async fn handle_delete_course(
_app_state: web::Data<AppState>,
_path: web::Path<(i32, i32)>,
) -> Result<HttpResponse, Error> {
// TODO: 呼叫資料庫函式,刪除對應的課程
Ok(HttpResponse::Ok().body("成功刪除課程"))
}
圖表解說:課程管理 API 處理流程
此活動圖展示了從使用者登入到管理(新增、更新、刪除)課程的完整操作流程。
@startuml
!theme _none_
skinparam dpi auto
skinparam defaultFontName "Microsoft JhengHei UI"
skinparam minClassWidth 100
skinparam defaultFontSize 16
title 課程管理活動圖
start
:使用者登入;
if (登入成功?) then (yes)
:進入課程管理頁面;
partition 課程操作 {
fork
:<b>新增課程</b>\n(POST /courses/{tutor_id});
fork again
:<b>更新課程</b>\n(PUT /courses/{tutor_id}/{course_id});
fork again
:<b>刪除課程</b>\n(DELETE /courses/{tutor_id}/{course_id});
end fork
}
:API Handler 呼叫資料庫服務;
:資料庫執行操作;
:回傳操作結果;
else (no)
:顯示錯誤訊息;
endif
stop
@enduml
步驟五:測試 API 端點
在啟動伺服器 (cargo run) 後,我們可以使用 curl 等工具來測試我們新建立的 API 端點是否正常運作。
# 測試新增課程
curl -X POST -H "Content-Type: application/json" \
-d '{"course_name":"Rust入門","course_description":"基礎教學"}' \
localhost:8080/courses/1
# 測試更新課程
curl -X PUT -H "Content-Type: application/json" \
-d '{"course_name":"Rust進階"}' \
localhost:8080/courses/1/1
# 測試刪除課程
curl -X DELETE localhost:8080/courses/1/1
執行這些指令後,您應該能在伺服器的控制台中看到對應的 “Got … request” 訊息,證明我們的路由和 handler 已經成功設定。接下來的工作就是填充這些 handler 的內部邏輯,與資料庫進行真實的互動。