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)
}
內容解密:
- 查詢陳述式:使用
sqlx::query!巨集來建立 SQL 查詢陳述式,查詢特定tutor_id的導師資料。 - 引數繫結:使用
$1來繫結tutor_id引數,防止 SQL 注入攻擊。 - 資料提取:使用
fetch_one方法從資料函式庫中提取一筆資料,若無資料則傳回錯誤。 - 錯誤處理:使用
map_err將sqlx錯誤轉換為自定義的EzyTutorError型別。 - 結果對映:將查詢結果對映到
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,
})
}
內容解密:
- 插入陳述式:使用
sqlx::query!巨集建立 SQL 插入陳述式,將新的導師資料插入ezy_tutor_c6表格中。 - 傳回插入結果:使用
returning子句傳回插入的資料,包括自動生成的tutor_id。 - 引數繫結:使用
$1、$2、$3繫結new_tutor的屬性,防止 SQL 注入攻擊。 - 結果處理:將插入結果對映到
Tutor結構體並傳回。
資料函式庫指令碼設計
以下是建立 ezy_tutor_c6 和 ezy_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_c6 和 ezy_course_c6 兩個表格的結構以及它們之間的關聯。ezy_course_c6 表格中的 tutor_id 欄位參考了 ezy_tutor_c6 表格中的 tutor_id,形成了一對多的關係。
@startuml
note
無法自動轉換的 Plantuml 圖表
請手動檢查和調整
@endumlAPI 測試與執行
執行以下命令來測試 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." }'
內容解密:
- 啟動服務:使用
cargo run命令啟動 Web 服務。 - 測試 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 函式。