ข้ามไปยังเนื้อหา

Extractors

ใน Express คุณเข้าถึง route parameter ผ่าน object req.params ที่ไม่มี type ส่วน Axum เปลี่ยนมาใช้ Path<T> extractor แทน — deserialize URL segment ตรงเข้า Rust type และถ้าค่าไม่ตรงก็ล้มเหลวตั้งแต่ระดับ type

TypeScript
// Express
app.get('/users/:id', async (req, res) => {
const id = req.params.id; // string, always
const numId = parseInt(id, 10); // manual parse, can fail at runtime
const user = await db.findUser(numId);
res.json(user);
});
Rust
use axum::{extract::Path, Json};
use uuid::Uuid;
async fn get_user(Path(id): Path<Uuid>) -> Json<User> {
// id is already a Uuid — Axum returns 400 automatically
// if the path segment is not a valid UUID
let user = db_find_user(id).await;
Json(user)
}

Express เปิดเผย query string parameters ผ่าน req.query — ซึ่งก็ไม่มี type เช่นกัน Query<T> ของ Axum deserialize query string ไปยัง struct โดยใช้ serde

TypeScript
// Express
app.get('/users', async (req, res) => {
const page = parseInt(req.query.page as string) || 1;
const limit = parseInt(req.query.limit as string) || 30;
const users = await db.listUsers({ page, limit });
res.json(users);
});
Rust
use axum::extract::Query;
use serde::Deserialize;
#[derive(Deserialize)]
struct Pagination {
#[serde(default = "default_page")]
page: u32,
#[serde(default = "default_limit")]
limit: u32,
}
fn default_page() -> u32 { 1 }
fn default_limit() -> u32 { 30 }
async fn list_users(Query(pagination): Query<Pagination>) -> Json<Vec<User>> {
let users = db_list_users(pagination.page, pagination.limit).await;
Json(users)
}

Express วิเคราะห์ JSON bodies ด้วย express.json() middleware และแนบผลลัพธ์กับ req.body — ซึ่งยังคงไม่มี type จนกว่าคุณจะ cast ด้วยตนเอง Json<T> extractor ของ Axum deserialize body โดยตรงไปยัง struct ของคุณ ถ้าขาด required field จะได้รับ 422 Unprocessable Entity โดยอัตโนมัติ

TypeScript
// Express + zod for validation
import { z } from 'zod';
const CreateUserSchema = z.object({
name: z.string(),
email: z.string().email(),
});
app.post('/users', async (req, res) => {
const body = CreateUserSchema.parse(req.body); // throws if invalid
const user = await db.createUser(body);
res.status(201).json(user);
});
Rust
use axum::{http::StatusCode, Json};
use serde::{Deserialize, Serialize};
#[derive(Deserialize)]
struct CreateUser {
name: String,
email: String,
}
#[derive(Serialize)]
struct User {
id: u32,
name: String,
email: String,
}
async fn create_user(
Json(payload): Json<CreateUser>,
) -> (StatusCode, Json<User>) {
// payload is guaranteed to have name and email
let user = User { id: 42, name: payload.name, email: payload.email };
(StatusCode::CREATED, Json(user))
}

handler เดียวสามารถรับ extractors หลายตัวเป็น parameters แยกกัน Axum จะเรียกทั้งหมดก่อนที่ handler body จะทำงาน

use axum::{
extract::{Path, Query, State},
Json,
};
use uuid::Uuid;
async fn get_user_posts(
State(db): State<DbPool>,
Path(user_id): Path<Uuid>,
Query(pagination): Query<Pagination>,
) -> Json<Vec<Post>> {
let posts = db.user_posts(user_id, pagination.page, pagination.limit).await.unwrap();
Json(posts)
}

รันบนเครื่องของคุณ — extractors ทำงานได้เฉพาะในบริบทของ Axum router ที่กำลังทำงาน ใช้ cargo run และทดสอบด้วย curl

`Path(id): Path<Uuid>` ใน Axum handler ทำอะไร?
ควรใช้ extractor ใดสำหรับ JSON request bodies?
Axum คืน HTTP status อะไรเมื่อ `Json<T>` body ไม่สามารถ deserialize ได้?
ใน Axum คุณอ่าน query string parameters พร้อม default values อย่างไร?