在現代 Web 開發中,前後端分離的架構已成為主流。後端負責提供 RESTful API,以結構化的 JSON 格式交換資料;前端則負責呼叫這些 API 並將資料渲染成使用者介面。本文將以 Rust 的 Actix-web 框架為例,引導您完成一個「全端」應用程式的最小可行性產品 (MVP),涵蓋從建構後端 API 到與前端頁面整合的完整流程。
步驟一:建構後端 REST API
我們的第一步是建立一個 Actix-web 伺服器,並提供一個回傳貓咪列表的 API 端點。
1. 專案設定與依賴
首先,建立一個新的 Rust 專案,並加入 actix-web 和 serde 依賴。
cargo new catdex-api-server
cd catdex-api-server
cargo add actix-web
cargo add serde --features derive
2. 定義資料模型與 API Handler
在 src/main.rs 中,我們定義 Cat 結構體,並編寫一個 handler 函式 cats_endpoint,它將回傳一個硬編碼 (hardcoded) 的貓咪資料列表。
use actix_web::{web, App, HttpServer, Responder};
use serde::Serialize;
// 定義 Cat 資料模型,並讓它可以被序列化為 JSON
#[derive(Serialize)]
pub struct Cat {
pub id: i32,
pub name: String,
pub image_path: String,
}
// API Handler: /api/cats
async fn cats_endpoint() -> impl Responder {
let cats = vec![
Cat {
id: 1,
name: "英國短毛貓 (British Shorthair)".to_string(),
image_path: "/images/british-shorthair.jpg".to_string(),
},
Cat {
id: 2,
name: "波斯貓 (Persian)".to_string(),
image_path: "/images/persian.jpg".to_string(),
},
Cat {
id: 3,
name: "布偶貓 (Ragdoll)".to_string(),
image_path: "/images/ragdoll.jpg".to_string(),
},
];
// 使用 web::Json 將 Vec<Cat> 轉換為 JSON 回應
web::Json(cats)
}
程式碼解說:
#[derive(Serialize)]: 這個屬性宏讓serde能自動為Cat結構體產生將其轉換為 JSON 的程式碼。web::Json(cats): 這是一個Responder,它會自動將傳入的cats變數序列化為 JSON 字串,並設定正確的 HTTP 標頭 (Content-Type: application/json)。
步驟二:提供靜態檔案服務
為了讓瀏覽器能存取我們的 HTML、CSS 和圖片,我們需要讓 Actix-web 伺服器也能提供靜態檔案服務。
1. 專案設定與依賴
加入 actix-files 依賴:
cargo add actix-files
並在專案根目錄建立 static/ 和 static/images/ 資料夾,將前端相關檔案放入其中。
2. 整合靜態檔案與 API 路由
我們更新 main 函式,加入 actix_files::Files 服務來提供靜態檔案,並同時註冊我們的 API 路由。
use actix_files::Files;
// ... (保留其他 use)
#[actix_web::main]
async fn main() -> std::io::Result<()> {
println!("正在監聽 http://127.0.0.1:8080");
HttpServer::new(|| {
App::new()
// API 路由
.service(
web::scope("/api")
.route("/cats", web::get().to(cats_endpoint))
)
// 靜態檔案服務
.service(Files::new("/images", "static/images").show_files_listing())
.service(Files::new("/", "static/").index_file("index.html"))
})
.bind("127.0.0.1:8080")?
.run()
.await
}
程式碼解說:
web::scope("/api"): 將所有 API 相關的路由歸納在/api路徑下,這是一個良好的實踐。Files::new(...): 設定靜態檔案服務。我們設定了兩個服務:一個用於/images路徑,另一個用於根路徑/,並指定index.html為預設檔案。
步驟三:前端整合
現在,後端已經準備就緒,我們可以編寫前端頁面來呼叫 API 並展示資料。
編寫 index.html
在 static/index.html 中,我們使用 JavaScript 的 fetch API 來非同步地獲取貓咪資料。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Catdex</title>
</head>
<body>
<h1>Catdex</h1>
<section id="cats-container">
<p>正在載入貓咪資料...</p>
</section>
<script>
document.addEventListener("DOMContentLoaded", () => {
const container = document.getElementById("cats-container");
fetch('/api/cats')
.then(response => response.json())
.then(cats => {
container.innerHTML = ""; // 清空載入提示
cats.forEach(cat => {
const catElement = document.createElement("article");
catElement.innerHTML = `
<h3>${cat.name}</h3>
<img src="${cat.image_path}" alt="${cat.name}" width="200">
`;
container.appendChild(catElement);
});
})
.catch(error => {
console.error('無法獲取貓咪資料:', error);
container.innerText = "載入資料失敗。";
});
});
</script>
</body>
</html>
圖表解說:前後端互動流程
此循序圖展示了使用者打開網頁後,前端如何向後端請求資料並將其渲染出來的完整流程。
@startuml
!theme _none_
skinparam dpi auto
skinparam defaultFontName "Microsoft JhengHei UI"
skinparam minClassWidth 100
skinparam defaultFontSize 16
title 前後端資料互動循序圖
actor User
participant Browser
participant "Actix-web Server" as Server
User -> Browser : 訪問 /
Browser -> Server : GET / (請求 index.html)
Server --> Browser : 回應 HTML 內容
Browser -> Browser : 解析 HTML,執行 JavaScript
Browser -> Server : GET /api/cats (fetch API)
Server -> Server : 執行 cats_endpoint()
Server --> Browser : 回應 200 OK (含 JSON 資料)
Browser -> Browser : 解析 JSON,動態建立 HTML 元素
Browser -> User : 顯示渲染後的貓咪列表
@enduml
執行 cargo run 並訪問 http://localhost:8080,您將看到一個由 Rust 後端驅動、前端 JavaScript 渲染的動態頁面。這個範例雖然簡單,卻涵蓋了現代 Web 應用前後端分離架構的核心互動模式。