現代應用程式開發越來越傾向於無伺服器架構,Rust 語言的效能和安全性使其成為理想的開發工具。本文透過 AWS SDK for Rust,示範建構一個包含 Lambda 函式處理請求、DynamoDB 儲存資料,以及 S3 儲存檔案的無伺服器應用程式。程式碼範例涵蓋了 SDK 設定、客戶端初始化、請求處理、資料函式庫互動、S3 預簽名 URL 生成等關鍵步驟,並引導讀者完成前端網頁的佈署和 CORS 設定,確保前後端順利溝通。此外,文章也提供 S3 儲存桶設定、靜態網站託管和 CORS 相關的實作細節,方便讀者快速上手無伺服器應用開發。
整合 AWS SDK for Rust 實作無伺服器架構
在現代的雲端運算中,無伺服器架構已成為開發者的重要選擇之一。AWS 提供了一系列的服務來支援無伺服器架構的開發,而 Rust 語言因其高效能和安全性,成為了開發者的熱門選擇。本篇文章將介紹如何使用 AWS SDK for Rust 來實作無伺服器架構,並提供完整的程式碼範例。
設定 AWS SDK for Rust
首先,我們需要在 Rust 專案中加入 AWS SDK 的依賴。在 Cargo.toml 中新增以下內容:
[dependencies]
aws-config = "0.56.0"
aws-sdk-dynamodb = "0.23.0"
aws-sdk-s3 = "0.23.0"
tokio = { version = "1", features = ["full"] }
tracing-subscriber = "0.3.11"
初始化 AWS 客戶端
在 main 函式中,我們需要初始化 AWS 的客戶端,包括 DynamoDB 和 S3。
#[tokio::main]
async fn main() -> Result<(), Error> {
tracing_subscriber::fmt()
.with_max_level(tracing::Level::INFO)
.with_target(false)
.without_time()
.init();
let config = aws_config::load_from_env().await;
let dynamo_client = dynamodb::Client::new(&config);
let s3_client = s3::Client::new(&config);
let table_name = std::env::var("TABLE_NAME")?.to_string();
let bucket_name = std::env::var("BUCKET_NAME")?.to_string();
// ...
}
內容解密:
這段程式碼初始化了 AWS 的設定,並建立了 DynamoDB 和 S3 的客戶端。tracing_subscriber 用於設定日誌輸出。aws_config::load_from_env().await 從環境變數中載入 AWS 設定。dynamodb::Client::new(&config) 和 s3::Client::new(&config) 分別建立了 DynamoDB 和 S3 的客戶端。
建立 Lambda 函式
我們需要建立一個 Lambda 函式來處理 POST 請求,並生成 S3 的預簽名 URL。
async fn function_handler(
request: Request,
dynamo_client: &dynamodb::Client,
s3_client: &s3::Client,
table_name: &str,
bucket_name: &str,
) -> Result<Response, Error> {
// 處理請求並生成 S3 預簽名 URL
let presigned_url = s3_client
.put_object()
.bucket(bucket_name)
.key("cat_image.jpg")
.presigned(PresigningConfig::expires_in(Duration::from_secs(60)))
.await?;
// 將資料寫入 DynamoDB
dynamo_client
.put_item()
.table_name(table_name)
.item("id", AttributeValue::S("cat_id".to_string()))
.item("name", AttributeValue::S("cat_name".to_string()))
.send()
.await?;
// 傳回預簽名 URL
Ok(Response::builder()
.status(StatusCode::OK)
.header("Content-Type", "application/json")
.body(json!({ "upload_url": presigned_url.uri() }).to_string().into())
.unwrap())
}
內容解密:
這段程式碼定義了一個 Lambda 函式,處理 POST 請求並生成 S3 的預簽名 URL。s3_client.put_object() 用於生成預簽名 URL。dynamo_client.put_item() 將資料寫入 DynamoDB。最後,函式傳回包含預簽名 URL 的 JSON 回應。
佈署到 AWS
使用以下命令佈署到 AWS:
cargo lambda build --release --arm64
sam deploy
前端實作
在 client/dist 目錄下建立以下檔案:
index.html:顯示貓咪列表的頁面css/index.css:樣式表add.html:新增貓咪的表單頁面
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Catdex</title>
<link rel="stylesheet" href="css/index.css" type="text/css">
</head>
<body>
<h1>Catdex</h1>
<p>
<a href="/add.html">Add a new cat</a>
</p>
<section class="cats" id="cats">
<p>No cats yet</p>
</section>
<script charset="utf-8">
// ...
</script>
</body>
</html>
add.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Catdex</title>
<link rel="stylesheet" href="css/index.css" type="text/css">
</head>
<body>
<h1>Add a new cat</h1>
<form onsubmit="return submitForm(event)">
<label for="name">Name:</label>
<input type="text" name="name" id="name" value="" />
<!-- ... -->
</form>
<script>
async function submitForm(e) {
// ...
}
</script>
</body>
</html>
內容解密:
這兩個 HTML 檔案分別用於顯示貓咪列表和新增貓咪。index.html 使用 JavaScript fetch API 從 API 取得貓咪列表並顯示。add.html 提供了一個表單,用於新增貓咪並上傳圖片。
使用AWS SDK for Rust佈署靜態網站至S3
在前面的章節中,我們已經完成了Catdex應用程式的後端API開發。現在,我們需要將前端網頁佈署到AWS S3上,並組態適當的存取許可權。
建立S3 Bucket
首先,我們需要建立一個S3 Bucket來存放我們的靜態網站檔案。在Cargo.toml檔案中新增一個新的binary目標:
[[bin]]
name = "create-bucket"
path = "src/bin/create-bucket.rs"
然後,在src/bin/create-bucket.rs檔案中撰寫建立S3 Bucket的程式碼:
use aws_sdk_s3 as s3;
#[tokio::main]
async fn main() {
let config = aws_config::load_from_env().await;
let s3_client = s3::Client::new(&config);
// 建立S3 Bucket
let bucket_name = "catdex-frontend";
s3_client.create_bucket().bucket(bucket_name).send().await.unwrap();
}
上傳網站檔案至S3 Bucket
接下來,我們需要上傳網站檔案至S3 Bucket。在src/bin/put-website.rs檔案中撰寫上傳檔案的程式碼:
use aws_sdk_s3 as s3;
use aws_sdk_s3::model::{IndexDocument, WebsiteConfiguration};
use aws_sdk_s3::types::ByteStream;
use std::path::Path;
#[tokio::main]
async fn main() {
let config = aws_config::load_from_env().await;
let s3_client = s3::Client::new(&config);
// 上傳index.html
let body = ByteStream::from_path(Path::new("../../client/dist/index.html")).await.unwrap();
s3_client.put_object().body(body).bucket("catdex-frontend").key("index.html").content_type("text/html").send().await.unwrap();
// 上傳add.html
let body = ByteStream::from_path(Path::new("../../client/dist/add.html")).await.unwrap();
s3_client.put_object().body(body).bucket("catdex-frontend").key("add.html").content_type("text/html").send().await.unwrap();
// 上傳index.css
let body = ByteStream::from_path(Path::new("../../client/dist/css/index.css")).await.unwrap();
s3_client.put_object().body(body).bucket("catdex-frontend").key("css/index.css").content_type("text/css").send().await.unwrap();
// 設定S3 Bucket為靜態網站
let cfg = WebsiteConfiguration::builder().index_document(IndexDocument::builder().suffix("index.html").build()).build();
s3_client.put_bucket_website().bucket("catdex-frontend").website_configuration(cfg).send().await.unwrap();
// 設定S3 Bucket的存取許可權
s3_client.put_bucket_policy().bucket("catdex-frontend").policy(include_str!("../../bucket_policy.json")).send().await.unwrap();
}
程式碼解析:
- 載入AWS設定:使用
aws_config::load_from_env().await載入AWS設定。 - 建立S3 Client:使用
s3::Client::new(&config)建立S3 Client。 - 上傳檔案:使用
ByteStream::from_path讀取檔案內容,並使用s3_client.put_object上傳檔案至S3 Bucket。 - 設定靜態網站:使用
s3_client.put_bucket_website設定S3 Bucket為靜態網站。 - 設定存取許可權:使用
s3_client.put_bucket_policy設定S3 Bucket的存取許可權。
設定Bucket Policy
在專案根目錄下建立一個名為bucket_policy.json的檔案,用於設定S3 Bucket的存取許可權:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": [
"s3:GetObject"
],
"Resource": [
"arn:aws:s3:::catdex-frontend/*"
]
}
]
}
啟用CORS
為了讓前端網頁能夠存取後端API,我們需要在API Gateway中啟用CORS。在template.yaml檔案中新增以下設定:
Globals:
Function:
Timeout: 3
Api:
Cors:
AllowMethods: "'GET,POST'"
AllowHeaders: "'content-type'"
AllowOrigin: "'*'"
然後,在後端API的程式碼中新增Access-Control-Allow-Origin標頭:
// 在src/bin/lambda/get-cats.rs和src/bin/lambda/post-cat.rs中新增以下程式碼
let response = Response {
status_code: http::StatusCode::OK,
headers: vec![(http::header::ACCESS_CONTROL_ALLOW_ORIGIN, "*".to_string())],
body: serde_json::to_string(&cats).unwrap(),
};
CORS解析:
- CORS簡介:CORS(Cross-Origin Resource Sharing)是一種機制,允許網頁從不同的來源存取資源。
- 啟用CORS:在API Gateway中啟用CORS,並設定允許的HTTP方法、標頭和來源。
使用AWS SDK for Rust建立無伺服器架構的Catdex應用程式
在上一章中,我們探討瞭如何使用Rust建立Web服務。本章將進一步介紹如何使用AWS SDK for Rust建立無伺服器架構的Catdex應用程式。
設定CORS
為了使前端能夠與後端API進行互動,我們需要設定CORS(Cross-Origin Resource Sharing)。CORS是一種機制,允許網頁從不同的來源載入資源。
let resp = Response::builder()
.status(StatusCode::OK)
.header("content-type", "text/html")
.header("Access-Control-Allow-Origin", "*")
.header("Access-Control-Allow-Headers", "Content-Type")
.header("Access-Control-Allow-Methods", "GET")
.body(serde_json::to_string(&response_body).unwrap().into())
.map_err(Box::new)?;
Ok(resp)
內容解密:
.status(StatusCode::OK):設定HTTP回應狀態碼為200,表示請求成功。.header("content-type", "text/html"):設定回應的Content-Type為text/html,表示回應內容是HTML格式。.header("Access-Control-Allow-Origin", "*"):允許所有來源的請求存取該資源。.header("Access-Control-Allow-Headers", "Content-Type"):指定允許的請求標頭,本例中僅允許Content-Type標頭。.header("Access-Control-Allow-Methods", "GET"):指定允許的HTTP方法,本例中僅允許GET請求。
更新CORS設定
AWS SAM CLI預設的CORS設定允許來自https://*.amazonaws.com的PUT請求,但我們的網站是使用HTTP協定而非HTTPS。因此,我們需要使用AWS SDK重新設定CORS。
let cors_rule_1 = CorsRule::builder()
.allowed_headers("*")
.allowed_methods("PUT")
.allowed_methods("POST")
.allowed_origins("http://*.amazonaws.com")
.max_age_seconds(0)
.build();
let cors_rule_2 = CorsRule::builder()
.allowed_headers("*")
.allowed_methods("GET")
.allowed_origins("*")
.build();
let cfg = CorsConfiguration::builder()
.cors_rules(cors_rule_1)
.cors_rules(cors_rule_2)
.build();
s3_client
.put_bucket_cors()
.bucket(BUCKET)
.cors_configuration(cfg)
.send()
.await
.unwrap();
內容解密:
CorsRule::builder():建立一個CORS規則建構器。.allowed_headers("*"):允許所有請求標頭。.allowed_methods("PUT")和.allowed_methods("POST"):允許PUT和POST請求。.allowed_origins("http://*.amazonaws.com"):允許來自http://*.amazonaws.com的請求。.max_age_seconds(0):設定CORS設定的最大存活時間為0秒。s3_client.put_bucket_cors():更新S3儲存桶的CORS設定。
安全注意事項
在建立雲端應用程式時,安全性是一個非常重要的考量。我們在開發過程中簡化了一些安全設定的步驟,但在實際生產環境中,必須認真對待安全性問題。
下一步
在本章中,我們使用AWS SDK for Rust建立了一個無伺服器架構的Catdex應用程式。下一步,你可以考慮新增更多功能到前端,或者使用AWS CloudFront來提供更好的效能。你也可以嘗試使用EC2和RDS來建立一個傳統的伺服器和SQL資料函式庫Web模型。