想自動生成帶注釋的 Cargo.toml?用「代碼生成」提示詞,30 秒即可拿到模板,再交給「代碼優化」砍掉冗余特性,編譯速度提升 20%!
| 組件 | 用途 | 版本 |
|---|---|---|
| shuttle-service | 零運維部署平臺 | 0.46.x |
| axum | Web 框架 | 0.7.x |
| sqlx | 異步 PostgreSQL 驅動 | 0.7.x |
| serde | JSON 序列化 | 1.0 |
| tokio | 異步運行時 | 1.x |
# 安裝 Shuttle CLI
cargo install cargo-shuttle
# 創建項目
cargo shuttle init --name shuttle-example-axum
cd shuttle-example-axum
骨架:
src/
├── main.rs # 入口 + 路由
├── db.rs # 數據庫連接
├── models.rs # 實體與 JSON
└── migrations.sql # 建表腳本
use shuttle_shared_db::Postgres;
use sqlx::PgPool;
#[shuttle_runtime::main]
async fn axum(
#[Postgres] pool: PgPool, // ← 自動注入
) -> shuttle_axum::ShuttleAxum {
// 運行遷移
sqlx::migrate!()
.run(&pool)
.await
.expect("migrate failed");
let state = AppState { db: pool };
Ok(router(state).into())
}
migrations.sqlCREATE TABLE IF NOT EXISTS records (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
email TEXT NOT NULL
);
?? AI 審查
把「SQL 注入風險」提交評審?「代碼審查助手」可自動檢查參數綁定、拼接語句,提前發現 80% 潛在 Bug!
// src/models.rs
use serde::{Deserialize, Serialize};
use sqlx::FromRow;
#[derive(Debug, Serialize, Deserialize, FromRow)]
pub struct Record {
pub id: i32,
pub name: String,
pub email: String,
}
#[derive(Debug, Deserialize)]
pub struct CreateRecord {
pub name: String,
pub email: String,
}
#[derive(Debug, Deserialize)]
pub struct UpdateRecord {
pub name: Option<String>,
pub email: Option<String>,
}
// src/main.rs
use axum::{
extract::{Path, State, Json},
http::StatusCode,
response::IntoResponse,
routing::{get, post, put, delete},
Router,
};
use sqlx::PgPool;
#[derive(Clone)]
struct AppState {
db: PgPool,
}
async fn create_record(
State(state): State<AppState>,
Json(payload): Json<CreateRecord>,
) -> impl IntoResponse {
let rec = sqlx::query_as!(
Record,
"INSERT INTO records (name, email) VALUES ($1, $2) RETURNING *",
payload.name, payload.email
)
.fetch_one(&state.db)
.await
.unwrap();
(StatusCode::CREATED, Json(rec))
}
async fn get_record_by_id(
State(state): State<AppState>,
Path(id): Path<i32>,
) -> impl IntoResponse {
let rec = sqlx::query_as!(
Record,
"SELECT * FROM records WHERE id = $1",
id
)
.fetch_optional(&state.db)
.await
.unwrap();
match rec {
Some(r) => (StatusCode::OK, Json(r)),
None => (StatusCode::NOT_FOUND, Json("")),
}
}
async fn update_record(
State(state): State<AppState>,
Path(id): Path<i32>,
Json(payload): Json<UpdateRecord>,
) -> impl IntoResponse {
// 動態構建 SET 子句
let mut sql = String::from("UPDATE records SET ");
let mut args: Vec<String> = vec![];
if let Some(name) = payload.name {
sql.push_str("name = $1,");
args.push(name);
}
if let Some(email) = payload.email {
sql.push_str("email = $2,");
args.push(email);
}
sql.truncate(sql.len() - 1); // 去掉末尾逗號
sql.push_str(" WHERE id = $3 RETURNING *");
let rec = sqlx::query_as::<_, Record>(&sql)
.bind(args.get(0).unwrap_or(&"".to_string()))
.bind(args.get(1).unwrap_or(&"".to_string()))
.bind(id)
.fetch_one(&state.db)
.await
.unwrap();
(StatusCode::OK, Json(rec))
}
? AI 補救
把「動態 SQL 拼接」寫進注釋太麻煩?用「代碼文檔生成器」提示詞,自動在函數頭部生成標準注釋,提醒后續接入sqlx-query-builder,文檔一鍵達標!
fn router(state: AppState) -> Router {
Router::new()
.route("/records", post(create_record).get(list_records))
.route("/records/:id", get(get_record_by_id).put(update_record).delete(delete_record))
.with_state(state)
}
cargo shuttle run
# 默認端口 8000
cargo shuttle deploy --allow-dirty
# 自動生成域名:https://shuttle-example-axum.shuttleapp.rs
# 新增
curl -X POST https://<your-domain>.shuttleapp.rs/records \
-H "Content-Type: application/json" \
-d '{"name":"Alice","email":"alice@example.com"}'
# 查詢
curl https://<your-domain>.shuttleapp.rs/records/1
| 方向 | 工具/庫 |
|---|---|
| 自定義錯誤 | thiserror + axum::response::IntoResponse |
| 測試 | testcontainers + axum-test-helper |
| 分頁 | sqlx-query-builder + limit/offset |
| JWT 認證 | jsonwebtoken + axum-extra |
| OpenAPI | utoipa + swagger-ui |
通過本教程,你已掌握:
下一步可添加認證、分頁、OpenAPI 文檔等功能,打造生產級后端。祝編碼愉快!
今晚就 push 到 GitHub,明天就能用 Shuttle 一鍵上線!??
原文鏈接: https://www.shuttle.dev/blog/2024/01/31/write-a-rest-api-rust