Extractors
จาก req.params ถึง Path<T>
หัวข้อที่มีชื่อว่า “จาก req.params ถึง Path<T>”ใน Express คุณเข้าถึง route parameter ผ่าน object req.params ที่ไม่มี type ส่วน Axum เปลี่ยนมาใช้ Path<T> extractor แทน — deserialize URL segment ตรงเข้า Rust type และถ้าค่าไม่ตรงก็ล้มเหลวตั้งแต่ระดับ type
// Expressapp.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);});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)}จาก req.query ถึง Query<T>
หัวข้อที่มีชื่อว่า “จาก req.query ถึง Query<T>”Express เปิดเผย query string parameters ผ่าน req.query — ซึ่งก็ไม่มี type เช่นกัน Query<T> ของ Axum deserialize query string ไปยัง struct โดยใช้ serde
// Expressapp.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);});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)}จาก req.body ถึง Json<T>
หัวข้อที่มีชื่อว่า “จาก req.body ถึง Json<T>”Express วิเคราะห์ JSON bodies ด้วย express.json() middleware และแนบผลลัพธ์กับ req.body — ซึ่งยังคงไม่มี type จนกว่าคุณจะ cast ด้วยตนเอง Json<T> extractor ของ Axum deserialize body โดยตรงไปยัง struct ของคุณ ถ้าขาด required field จะได้รับ 422 Unprocessable Entity โดยอัตโนมัติ
// Express + zod for validationimport { 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);});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))}การรวม extractors หลายตัว
หัวข้อที่มีชื่อว่า “การรวม extractors หลายตัว”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