ปลายทางและแบบจำลอง
เส้นทางและข้อมูล
ปลายทางและแบบจำลอง เป็นบทเรียน Learn Rust Coding ฟรีบน CoddyKit นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Learn Rust Coding และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Learn Rust Coding มีบทเรียนทั้งหมด 4 บทเรียน
เส้นทางและแบบจำลองข้อมูล
API ถูกกำหนดด้วยจุดปลายทาง (URL ร่วมกับเมธอด HTTP) และแบบจำลอง (รูปแบบข้อมูล) ที่ไหลผ่านจุดเหล่านั้น ในบทเรียนนี้ คุณจะกำหนดแบบจำลองคำขอและการตอบกลับด้วย serde และเชื่อมเส้นทางลักษณะ CRUD ใน Axum
การกำหนดแบบจำลอง
แบบจำลองคือโครงสร้าง Rust ธรรมดา สร้าง Serialize เพื่อแปลงเป็น JSON ในการตอบกลับ และสร้าง Deserialize เพื่อแยกวิเคราะห์จากเนื้อหาคำขอ ชื่อฟิลด์จะจับคู่กับคีย์ JSON โดยตรง
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize, Clone)]
struct Todo {
id: u32,
title: String,
done: bool,
}แยกชนิดคำขอและการตอบกลับ
ไคลเอนต์ไม่ควรส่ง id ที่เซิร์ฟเวอร์กำหนดเองเมื่อสร้างทรัพยากร ให้ใช้โครงสร้างข้อมูลนำเข้าแยกต่างหากสำหรับเนื้อหาคำขอ และใช้แบบจำลองเต็มรูปแบบสำหรับการตอบกลับ วิธีนี้ทำให้สัญญาชัดเจนและป้องกันไม่ให้ไคลเอนต์ตั้งค่าฟิลด์ที่ไม่ควรตั้ง
use serde::Deserialize;
#[derive(Deserialize)]
struct CreateTodo {
title: String,
}
// The handler assigns the id and sets done = false.GET: แสดงรายการทรัพยากร
ตัวจัดการ GET /todos ส่งคืนชุดข้อมูลทั้งหมดเป็นอาร์เรย์ JSON โดยอ่านสถานะร่วม โคลนข้อมูลออกจากตัวล็อกอย่างรวดเร็ว แล้วห่อข้อมูลด้วย Json
use axum::{extract::State, Json};
use std::sync::{Arc, Mutex};
type Store = Arc<Mutex<Vec<Todo>>>;
#[derive(Clone, serde::Serialize)]
struct Todo { id: u32, title: String, done: bool }
async fn list_todos(State(store): State<Store>) -> Json<Vec<Todo>> {
let todos = store.lock().unwrap().clone();
Json(todos)
}POST: สร้างทรัพยากร
ตัวจัดการ POST /todos อ่านเนื้อหา JSON ด้วยตัวดึงข้อมูล Json กำหนด id ใหม่ จัดเก็บรายการ แล้วส่งคืนพร้อมสถานะ 201 Created ทูเพิล (StatusCode, Json<T>) ช่วยให้คุณกำหนดทั้งสถานะและเนื้อหาได้
use axum::{extract::State, Json, http::StatusCode};
async fn create_todo(
State(store): State<Store>,
Json(input): Json<CreateTodo>,
) -> (StatusCode, Json<Todo>) {
let mut todos = store.lock().unwrap();
let id = todos.len() as u32 + 1;
let todo = Todo { id, title: input.title, done: false };
todos.push(todo.clone());
(StatusCode::CREATED, Json(todo))
}พารามิเตอร์ในเส้นทาง
หากต้องการดึงทรัพยากรหนึ่งรายการ ให้จับส่วนหนึ่งของ URL ด้วยพารามิเตอร์ในเส้นทาง ประกาศพารามิเตอร์ในเส้นทางเป็น /todos/{id} และดึงค่าด้วยตัวดึงข้อมูล Path ชนิดที่คุณระบุ (ในที่นี้คือ u32) จะถูกแยกวิเคราะห์โดยอัตโนมัติ
use axum::extract::{Path, State};
use axum::{Json, http::StatusCode};
async fn get_todo(
State(store): State<Store>,
Path(id): Path<u32>,
) -> Result<Json<Todo>, StatusCode> {
let todos = store.lock().unwrap();
match todos.iter().find(|t| t.id == id) {
Some(t) => Ok(Json(t.clone())),
None => Err(StatusCode::NOT_FOUND),
}
}พารามิเตอร์คำค้น
การกรองและการแบ่งหน้าใช้สตริงคำค้น เช่น /todos?done=true ดึงค่าด้วยตัวดึงข้อมูล Query ลงในโครงสร้าง Deserialize ฟิลด์ที่อาจมีหรือไม่มีก็ใช้ Option เพื่อให้ไม่เป็นไรเมื่อไม่มีพารามิเตอร์
use axum::extract::{Query, State};
use axum::Json;
use serde::Deserialize;
#[derive(Deserialize)]
struct Filter { done: Option<bool> }
async fn filtered(
State(store): State<Store>,
Query(f): Query<Filter>,
) -> Json<Vec<Todo>> {
let todos = store.lock().unwrap();
let out = todos.iter()
.filter(|t| f.done.map_or(true, |d| t.done == d))
.cloned().collect();
Json(out)
}PUT และ DELETE
การอัปเดตใช้ PUT /todos/{id} พร้อมเนื้อหา ส่วนการลบใช้ DELETE /todos/{id} ทั้งสองแบบค้นหารายการตาม id และส่งคืน 404 หากไม่พบ โดยทั่วไปการลบที่สำเร็จจะส่งคืน 204 No Content
use axum::extract::{Path, State};
use axum::http::StatusCode;
async fn delete_todo(
State(store): State<Store>,
Path(id): Path<u32>,
) -> StatusCode {
let mut todos = store.lock().unwrap();
let before = todos.len();
todos.retain(|t| t.id != id);
if todos.len() < before { StatusCode::NO_CONTENT }
else { StatusCode::NOT_FOUND }
}เชื่อมเส้นทางเข้าด้วยกัน
ลงทะเบียนตัวจัดการทุกตัวบนตัวกำหนดเส้นทาง จัดกลุ่มเมธอดบนเส้นทางร่วมกัน: /todos จัดการการแสดงรายการและการสร้าง ส่วน /todos/{id} จัดการการดึง การอัปเดต และการลบ จากนั้นแนบที่เก็บข้อมูลร่วมด้วย .with_state
use axum::{routing::get, Router};
fn build(store: Store) -> Router {
Router::new()
.route("/todos", get(list_todos).post(create_todo))
.route("/todos/{id}",
get(get_todo).delete(delete_todo))
.with_state(store)
}ตรวจสอบข้อมูลนำเข้า
อย่าเชื่อถือข้อมูลจากไคลเอนต์โดยไม่ตรวจสอบ ตรวจสอบข้อมูลภายในตัวจัดการและส่งคืน 400 Bad Request เมื่อข้อมูลไม่ถูกต้อง ในที่นี้เราปฏิเสธชื่อเรื่องที่ว่างเปล่าก่อนจัดเก็บสิ่งใด เพื่อป้องกันข้อมูลเสียไม่ให้เข้าสู่ระบบ
use axum::{Json, extract::State, http::StatusCode};
async fn create_validated(
State(store): State<Store>,
Json(input): Json<CreateTodo>,
) -> Result<(StatusCode, Json<Todo>), StatusCode> {
if input.title.trim().is_empty() {
return Err(StatusCode::BAD_REQUEST);
}
let mut todos = store.lock().unwrap();
let id = todos.len() as u32 + 1;
let todo = Todo { id, title: input.title, done: false };
todos.push(todo.clone());
Ok((StatusCode::CREATED, Json(todo)))
}การตอบกลับข้อผิดพลาดอย่างสม่ำเสมอ
การส่งคืนเพียงรหัสสถานะก็ใช้งานได้ แต่ API สำหรับระบบจริงควรส่งเนื้อหาข้อผิดพลาดในรูป JSON ด้วย แนวทางทั่วไปคือสร้าง перечисข้อผิดพลาดแบบกำหนดเองที่ทำตาม IntoResponse ได้ โดยจับคู่แต่ละรูปแบบกับสถานะและข้อความ วิธีนี้ทำให้ไคลเอนต์ได้รับข้อผิดพลาดที่คาดเดาได้และเครื่องอ่านได้
use axum::response::{IntoResponse, Response};
use axum::http::StatusCode;
use axum::Json;
use serde_json::json;
enum ApiError { NotFound, BadRequest(String) }
impl IntoResponse for ApiError {
fn into_response(self) -> Response {
let (status, msg) = match self {
ApiError::NotFound => (StatusCode::NOT_FOUND, "not found".to_string()),
ApiError::BadRequest(m) => (StatusCode::BAD_REQUEST, m),
};
(status, Json(json!({ "error": msg }))).into_response()
}
}ตรวจสอบความเข้าใจอย่างรวดเร็ว
ทดสอบความเข้าใจเกี่ยวกับจุดปลายทางและแบบจำลอง
สรุปทบทวน
คุณได้กำหนดจุดปลายทางและแบบจำลอง:
- แบบจำลองคือโครงสร้างที่สร้าง
Serialize/Deserializeและควรใช้ชนิดข้อมูลนำเข้าแยกต่างหาก Json,Path,QueryและStateใช้ดึงข้อมูลคำขอ- จับคู่ CRUD กับ GET/POST/PUT/DELETE พร้อมรหัสสถานะที่เหมาะสม
- ตรวจสอบข้อมูลนำเข้าและส่งคืน
400เมื่อข้อมูลไม่ถูกต้อง - ชนิดข้อผิดพลาดแบบกำหนดเองที่ทำตาม
IntoResponseได้ ช่วยให้ข้อผิดพลาด JSON มีรูปแบบสม่ำเสมอ
คำถามที่พบบ่อย
บทเรียน “ปลายทางและแบบจำลอง” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “ปลายทางและแบบจำลอง” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Learn Rust Coding ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Learn Rust Coding มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “ปลายทางและแบบจำลอง”
เส้นทางและข้อมูล คุณปฏิบัติ Learn Rust Coding ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Learn Rust Coding หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน Learn Rust Coding บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน
บทเรียน “ปลายทางและแบบจำลอง” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน Learn Rust Coding นี้ได้ไหม
ได้ บทเรียน Learn Rust Coding ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- การตั้งค่าโครงการ
- ปลายทางและแบบจำลอง
- การผสานรวมฐานข้อมูล
- การทดสอบ API