返回文章列表

使用 Actix-web 構建 REST API 並整合前端

本文為一份 Actix-web 全端整合教學,指導如何使用 Rust 構建一個 RESTful API,並與一個簡單的前端頁面進行整合。內容涵蓋 API 路由設計、JSON 資料處理、靜態檔案服務,以及前端如何使用 JavaScript 的 `fetch` API 呼叫後端,實現完整的資料互動流程。

Web 開發 Rust

在現代 Web 開發中,前後端分離的架構已成為主流。後端負責提供 RESTful API,以結構化的 JSON 格式交換資料;前端則負責呼叫這些 API 並將資料渲染成使用者介面。本文將以 Rust 的 Actix-web 框架為例,引導您完成一個「全端」應用程式的最小可行性產品 (MVP),涵蓋從建構後端 API 到與前端頁面整合的完整流程。

步驟一:建構後端 REST API

我們的第一步是建立一個 Actix-web 伺服器,並提供一個回傳貓咪列表的 API 端點。

1. 專案設定與依賴

首先,建立一個新的 Rust 專案,並加入 actix-webserde 依賴。

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 應用前後端分離架構的核心互動模式。