Rust 的 Actix Web 框架提供了一個高效能且安全的 Web 開發環境。本文示範瞭如何使用 sqlx 函式庫操作 PostgreSQL 資料函式庫,包含查詢、新增、更新和刪除等功能,並解析了相關的 SQL 指令碼設計和引數繫結技巧,以防止 SQL 注入攻擊。此外,也說明瞭如何使用 Actix Web 建立路由、處理錯誤以及提供靜態檔案服務。文章更進一步探討了伺服器端渲染和單頁應用程式的概念,並以程式碼範例展示如何使用 Tera 範本引擎渲染動態網頁、處理使用者輸入和顯示資料清單。

資料函式庫操作與 Tutor API 實作

本章節將探討 Tutor API 的資料函式庫操作實作,包括查詢、新增、更新和刪除等功能。我們將逐步分析相關程式碼,並討論資料函式庫指令碼的設計與執行。

資料函式庫查詢操作

get_tutor_db 函式中,我們使用 sqlx 函式庫來執行資料函式庫查詢。以下為該函式的部分程式碼:

pub async fn get_tutor_db(pool: &PgPool, tutor_id: i32) -> Result<Tutor, EzyTutorError> {
    let tutor_row = sqlx::query!("select tutor_id, tutor_name, tutor_pic_url, tutor_profile from ezy_tutor_c6 where tutor_id = $1", tutor_id)
        .fetch_one(pool)
        .await
        .map(|tutor_row| Tutor {
            tutor_id: tutor_row.tutor_id,
            tutor_name: tutor_row.tutor_name,
            tutor_pic_url: tutor_row.tutor_pic_url,
            tutor_profile: tutor_row.tutor_profile,
        })
        .map_err(|_err| EzyTutorError::NotFound("Tutor id not found".into()))?;
    Ok(tutor_row)
}

內容解密:

  1. 查詢陳述式:使用 sqlx::query! 巨集來建立 SQL 查詢陳述式,查詢特定 tutor_id 的導師資料。
  2. 引數繫結:使用 $1 來繫結 tutor_id 引數,防止 SQL 注入攻擊。
  3. 資料提取:使用 fetch_one 方法從資料函式庫中提取一筆資料,若無資料則傳回錯誤。
  4. 錯誤處理:使用 map_errsqlx 錯誤轉換為自定義的 EzyTutorError 型別。
  5. 結果對映:將查詢結果對映到 Tutor 結構體。

新增 Tutor 資料

post_new_tutor_db 函式中,我們實作了新增導師資料的功能。以下是該函式的部分程式碼:

pub async fn post_new_tutor_db(pool: &PgPool, new_tutor: NewTutor) -> Result<Tutor, EzyTutorError> {
    let tutor_row = sqlx::query!("insert into ezy_tutor_c6 (tutor_name, tutor_pic_url, tutor_profile) values ($1, $2, $3) returning tutor_id, tutor_name, tutor_pic_url, tutor_profile", new_tutor.tutor_name, new_tutor.tutor_pic_url, new_tutor.tutor_profile)
        .fetch_one(pool)
        .await?;
    Ok(Tutor {
        tutor_id: tutor_row.tutor_id,
        tutor_name: tutor_row.tutor_name,
        tutor_pic_url: tutor_row.tutor_pic_url,
        tutor_profile: tutor_row.tutor_profile,
    })
}

內容解密:

  1. 插入陳述式:使用 sqlx::query! 巨集建立 SQL 插入陳述式,將新的導師資料插入 ezy_tutor_c6 表格中。
  2. 傳回插入結果:使用 returning 子句傳回插入的資料,包括自動生成的 tutor_id
  3. 引數繫結:使用 $1$2$3 繫結 new_tutor 的屬性,防止 SQL 注入攻擊。
  4. 結果處理:將插入結果對映到 Tutor 結構體並傳回。

資料函式庫指令碼設計

以下是建立 ezy_tutor_c6ezy_course_c6 表格的 SQL 指令碼:

create table ezy_tutor_c6 (
    tutor_id serial primary key,
    tutor_name varchar(200) not null,
    tutor_pic_url varchar(200) not null,
    tutor_profile varchar(2000) not null
);

create table ezy_course_c6 (
    course_id serial primary key,
    tutor_id INT not null,
    course_name varchar(140) not null,
    course_description varchar(2000),
    course_format varchar(30),
    course_structure varchar(200),
    course_duration varchar(30),
    course_price INT,
    course_language varchar(30),
    course_level varchar(30),
    posted_time TIMESTAMP default now(),
    CONSTRAINT fk_tutor FOREIGN KEY(tutor_id) REFERENCES ezy_tutor_c6(tutor_id) ON DELETE cascade
);

圖表翻譯:

此圖示呈現了 ezy_tutor_c6ezy_course_c6 兩個表格的結構以及它們之間的關聯。ezy_course_c6 表格中的 tutor_id 欄位參考了 ezy_tutor_c6 表格中的 tutor_id,形成了一對多的關係。

@startuml
note
  無法自動轉換的 Plantuml 圖表
  請手動檢查和調整
@enduml

API 測試與執行

執行以下命令來測試 Tutor API:

cargo run --bin iter5
curl -X POST localhost:3000/tutors/ -H "Content-Type: application/json" -d '{ "tutor_name":"Jessica", "tutor_pic_url": "http://tutor1.com/tutor1.pic", "tutor_profile": "Jessica is a great tutor." }'

內容解密:

  1. 啟動服務:使用 cargo run 命令啟動 Web 服務。
  2. 測試 API:使用 curl 命令測試新增導師的 API,傳送 JSON 資料到 /tutors/ 端點。

重構 Rust 與 Actix Web 程式碼:新增錯誤處理與功能增強

在開發 Web 服務的過程中,重構程式碼是一項重要的任務。本文將探討如何使用 Rust 與 Actix Web 框架來重構程式碼,並新增錯誤處理功能,以提升程式的穩定性與可維護性。

使用 curl 測試 API 端點

首先,我們可以使用 curl 指令來測試 API 端點。例如:

curl -X POST localhost:3000/tutors -H "Content-Type: application/json" -d '{"tutor_name":"John", "tutor_pic_url":"http://john.com/pic", "tutor_profile":"Experienced professional"}'
curl -X PUT localhost:3000/tutors/8 -H "Content-Type: application/json" -d '{"tutor_name":"James", "tutor_pic_url":"http://james.com/pic", "tutor_profile":"Expert in thermodynamics"}'
curl -X DELETE http://localhost:3000/tutors/8

內容解密:

  • 第一個 curl 指令用於建立新的導師記錄,透過 POST 請求將 JSON 資料傳送到 /tutors 端點。
  • 第二個指令更新導師 ID 為 8 的記錄,使用 PUT 請求並提供新的 JSON 資料。
  • 第三個指令刪除導師 ID 為 8 的記錄,使用 DELETE 請求。

瀏覽器中的 HTTP GET 請求

在瀏覽器中,我們可以直接輸入 URL 來執行 HTTP GET 請求,例如:

http://localhost:3000/tutors/
http://localhost:3000/tutors/2

內容解密:

  • 第一個 URL 用於檢索資料函式庫中所有導師的列表。
  • 第二個 URL 檢索導師 ID 為 2 的詳細資訊。

新增錯誤處理

為了提升使用者經驗,我們需要處理無效的 JSON 輸入。在 errors.rs 檔案中,我們新增了一個新的錯誤變體 InvalidInput(String)EzyTutorError 列舉中:

#[derive(Debug, Serialize)]
pub enum EzyTutorError {
    DBError(String),
    ActixError(String),
    NotFound(String),
    InvalidInput(String),
}

內容解密:

  • InvalidInput(String) 用於表示接收到的引數無效,並可攜帶一個字串訊息作為引數。

修改錯誤處理函式

我們還需要在 error_response 函式中新增對 EzyTutorError::InvalidInput 的處理邏輯:

fn error_response(&self) -> String {
    match self {
        EzyTutorError::DBError(msg) => {
            println!("Database error occurred: {:?}", msg);
            "Database error".into()
        }
        EzyTutorError::ActixError(msg) => {
            println!("Server error occurred: {:?}", msg);
            "Internal server error".into()
        }
        EzyTutorError::NotFound(msg) => {
            println!("Not found error occurred: {:?}", msg);
            msg.into()
        }
        EzyTutorError::InvalidInput(msg) => {
            println!("Invalid parameters received: {:?}", msg);
            msg.into()
        }
    }
}

內容解密:

  • 當遇到 InvalidInput 錯誤時,會列印錯誤訊息並傳回對應的錯誤回應。

設定 JSON 組態的錯誤處理器

在建立 Actix App 例項時,我們新增了一個 JSON 組態錯誤處理器,用於處理無效的 JSON 輸入:

let app = move || {
    App::new()
        .app_data(shared_data.clone())
        .app_data(web::JsonConfig::default().error_handler(|_err, _req| {
            EzyTutorError::InvalidInput("Please provide valid Json input".to_string()).into()
        }))
        .configure(general_routes)
        .configure(course_routes)
        .configure(tutor_routes)
};

內容解密:

  • 當接收到無效的 JSON 資料時,會傳回自定義的錯誤訊息。

使用 Rust 與 Actix Web 開發伺服器端網頁應用程式

本章涵蓋以下主題:

  • 使用 Actix 提供靜態網頁服務
  • 使用 Actix 和 Tera 渲染動態網頁
  • 新增使用者輸入表單
  • 使用範本顯示清單
  • 編寫並執行客戶端測試
  • 連線後端網路服務

在書的第3章到第6章中,我們使用 Rust 和 Actix Web 框架從零開始建立了導師網路服務。在本章中,我們將專注於學習如何在 Rust 中建立網路應用程式的基礎知識。使用系統程式語言來建立網路應用程式可能令人感到陌生,但這正是 Rust 的強大之處。它可以輕鬆地跨越系統程式設計和應用程式設計的世界。

在本章中,您將學習到可以用 Rust 建立網路應用程式的概念和工具。重要的是要回顧,建立網路應用程式有兩種主要技術:伺服器端渲染(SSR)和單頁應用程式(SPA),每種技術都可能採用漸進式網路應用程式(PWA)的形式。在本章中,我們將專注於前者,而在後面的章節中,我們將介紹後者。本文不會涵蓋 PWA。

更具體地說,第7章到第9章的重點是學習如何開發一個簡單的網路應用程式,可以用於註冊和登入網路應用程式、檢視清單和詳細資訊檢視,並使用根據網路的表單對資料執行標準的 CRUD(建立、讀取、更新和刪除)操作。在這個過程中,您將學習如何使用 Actix Web 框架和範本引擎來渲染動態網頁。雖然我們可以使用任何 Rust 網路框架來完成此任務,但在本文中,我們將專注於 Actix Web。

網路應用程式開發技術概述

在開發網路應用程式時,有兩種主要方法:伺服器端渲染(SSR)和單頁應用程式(SPA)。這兩種方法各有其優缺點。

伺服器端渲染(SSR)

伺服器端渲染是一種傳統的網路應用程式開發方法,其中網頁的 HTML 是在伺服器上生成的,然後傳送到客戶端的瀏覽器。這種方法的優點包括:

  • 更好的 SEO:由於搜尋引擎可以更容易地抓取伺服器端渲染的網頁,因此 SSR 對 SEO 更為友好。
  • 更快的初始載入:使用者可以更快地看到初始內容,因為瀏覽器不需要執行大量的 JavaScript 程式碼。

單頁應用程式(SPA)

單頁應用程式是一種現代的網路應用程式開發方法,其中網頁的內容是動態地在客戶端的瀏覽器中使用 JavaScript 生成的。這種方法的優點包括:

  • 更豐富的使用者經驗:SPA 可以提供更豐富的使用者經驗,因為它可以快速地更新內容而無需重新載入整個頁面。
  • 減少伺服器負載:伺服器只需要提供初始的 HTML 和靜態資源,後續的互動由客戶端的 JavaScript 處理。

本文的重點

在本文中,我們將專注於使用 Rust 和 Actix Web 框架開發伺服器端渲染的網路應用程式。我們將介紹如何使用範本引擎來渲染動態網頁,以及如何處理使用者輸入和顯示資料清單。

使用 Actix 提供靜態網頁服務

首先,我們將學習如何使用 Actix Web 框架提供靜態網頁服務。這涉及設定 Actix 以提供靜態檔案,如 HTML、CSS 和 JavaScript 檔案。

use actix_web::{web, App, HttpResponse, HttpServer, Responder};

async fn index() -> impl Responder {
    HttpResponse::Ok().body(include_str!("../static/index.html"))
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        App::new()
            .route("/", web::get().to(index))
    })
    .bind("127.0.0.1:8080")?
    .run()
    .await
}

#### 內容解密:

上述程式碼展示瞭如何使用 Actix Web 框架提供靜態網頁服務。其中,index 函式傳回一個包含靜態 HTML 內容的 HTTP 回應。main 函式設定了一個 Actix Web 伺服器,並將根路徑("/")對應到 index 函式。