
กำลังโหลด...

กำลังโหลด...

เหลืออีก 2 ช่องทาง — body กับ headers
body น้องใช้ไปแล้วตอนทำ POST แต่มีรายละเอียดที่ยังไม่รู้อีกเยอะ ส่วน headers จะเป็นตัวสำคัญมากตอนทำ Login ในคอร์สหน้า วันนี้ให้รู้จักไว้ก่อน
เป้าหมายของ Mission นี้: เข้าใจ req.body และ req.headers ให้ครบ แล้วสรุปกฎว่าเมื่อไหร่
ใช้ช่องทางไหน
จบ Mission นี้คุณจะ
req.body รองรับข้อมูลรูปแบบไหนบ้างreq.headers ได้POST /books/7?notify=true HTTP/1.1 <-- (1) params: 7 (2) query: notify=true
Host: localhost:3000
Content-Type: application/json <-- (3) headers
Authorization: Bearer eyJhbGciOi...
{ <-- (4) body
"title": "1984",
"author": "George Orwell"
}
flowchart LR
R["HTTP Request"] --> P["req.params<br/>ชิ้นไหน"]
R --> Q["req.query<br/>แบบไหน"]
R --> H["req.headers<br/>ข้อมูลกำกับ / ตัวตน"]
R --> B["req.body<br/>เนื้อข้อมูล"]
style P fill:#1e3a5f,stroke:#38bdf8,color:#fff
style Q fill:#3f2d5c,stroke:#a855f7,color:#fff
style H fill:#5c4d2d,stroke:#eab308,color:#fff
style B fill:#14532d,stroke:#22c55e,color:#fff
+-------------+-------------------------+---------------------+----------------------+
| ช่องทาง | ใช้เมื่อ | มองเห็นใน URL ไหม | ตัวอย่าง |
+-------------+-------------------------+---------------------+----------------------+
| req.params | ระบุว่าเป็นชิ้นไหน | เห็น | /books/7 |
| req.query | เงื่อนไขเสริม กรอง/เรียง | เห็น | ?sort=year&page=2 |
| req.body | ข้อมูลที่จะบันทึก/แก้ไข | ไม่เห็น | {"title": "1984"} |
| req.headers | ข้อมูลกำกับ / ตัวตน / รูปแบบ | ไม่เห็น | Authorization: ... |
+-------------+-------------------------+---------------------+----------------------+
ข้อ 1 — ข้อมูลลับห้ามอยู่ใน URL เด็ดขาด
❌ GET /login?username=somchai&password=123456
เพราะ URL จะถูกเก็บไว้ใน: ประวัติเบราว์เซอร์, log ของเซิร์ฟเวอร์, log ของ proxy, Referer header ที่ส่งต่อไปเว็บอื่น — รหัสผ่านจะกระจายไปทุกที่
✅ POST /login + body: { "username": "somchai", "password": "123456" }
ข้อ 2 — GET ไม่มี body (ทบทวนจาก Chapter 1) เงื่อนไขของ GET ต้องไปอยู่ใน query
ข้อ 3 — Token ไปกับ header ไม่ใช่ body เพราะทุก Request ต้องแนบไป จะยัดใส่ body ทุกครั้งก็ไม่ได้ (GET ไม่มี body) → Backend Explorer 2 Chapter 5 จะใช้จริง
req.body ให้ลึกขึ้นapp.use(express.json());
Middleware ตัวนี้จะแกะ body เฉพาะเมื่อ Header Content-Type เป็น application/json
เท่านั้น ถ้าส่งมาแบบอื่นจะไม่ทำอะไรเลย และ req.body จะเป็น {} ว่างเปล่า
รูปแบบ body ที่พบบ่อย
| Content-Type | ใช้ตอนไหน | ต้องเปิด middleware อะไร |
|---|---|---|
application/json |
API ทั่วไป (คอร์สนี้ใช้ตัวนี้) | express.json() |
application/x-www-form-urlencoded |
ฟอร์ม HTML แบบดั้งเดิม | express.urlencoded({ extended: true }) |
multipart/form-data |
อัปโหลดไฟล์ | ต้องใช้ library เสริม เช่น multer |
เปิดพร้อมกันได้ทั้งสองแบบ ไม่ตีกัน
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(express.json({ limit: "1mb" }));
ถ้าไม่จำกัด ใครส่ง body ขนาด 500MB มาก็ทำให้เซิร์ฟเวอร์ล่มได้
req.headersapp.get("/debug", (req, res) => {
console.log(req.headers);
res.json({
userAgent: req.headers["user-agent"], // เบราว์เซอร์อะไร
contentType: req.headers["content-type"], // ส่งข้อมูลมาแบบไหน
auth: req.headers["authorization"], // Token (Backend Explorer 2 Chapter 5)
custom: req.headers["x-api-key"], // header ที่เราตั้งเอง
});
});
กฎของ headers ที่ต้องรู้
Content-Type ก็ต้องอ่านด้วย req.headers["content-type"][] ไม่ใช่ . เพราะชื่อ header มีขีดกลาง (req.headers.content-type เป็น syntax error)x- เช่น x-api-key// PUT /books/7?notify=true
// Header: x-user-role: admin
// Body: { "title": "1984" }
app.put("/books/:id", (req, res) => {
const id = Number(req.params.id); // 7 <- ชิ้นไหน
const notify = req.query.notify === "true"; // true <- ตัวเลือกเสริม
const role = req.headers["x-user-role"]; // "admin" <- ตัวตน
const { title } = req.body; // "1984" <- ข้อมูลที่จะบันทึก
if (role !== "admin") {
return res.status(403).json({ error: "ต้องเป็น admin เท่านั้น" });
}
// ...
});
สังเกต
req.query.notify === "true"— query เป็น string เสมอ ดังนั้น"false"ก็ยัง truthy! ต้องเทียบกับข้อความ"true"ตรงๆ ไม่ใช่เขียนif (req.query.notify)
| ปัญหา | สาเหตุ | วิธีแก้ |
|---|---|---|
req.body เป็น {} |
Content-Type ไม่ใช่ application/json |
ตั้ง Header ให้ถูกตอนยิงทดสอบ |
req.headers.content-type พัง |
ชื่อมีขีดกลาง | ใช้ req.headers["content-type"] |
อ่าน req.headers["Authorization"] ไม่ได้ |
ต้องเป็นตัวพิมพ์เล็ก | req.headers["authorization"] |
?notify=false แล้วยังทำงาน |
string "false" เป็น truthy |
เทียบ === "true" |
| ส่งรหัสผ่านผ่าน query | ข้อมูลลับรั่วเข้า log/history | ใช้ POST + body เสมอ |
| ส่ง body ไปกับ GET แล้วอ่านไม่ได้ | GET ไม่มี body ตามมาตรฐาน | ย้ายไปใช้ query |
| ฟอร์ม HTML ส่งมาแล้ว body ว่าง | ฟอร์มส่งเป็น urlencoded ไม่ใช่ json | เพิ่ม express.urlencoded() |
params (ชิ้นไหน) / query (แบบไหน) / body (เนื้อข้อมูล) / headers (ข้อมูลกำกับ)params และ query มองเห็นใน URL → ห้ามใส่ความลับbody ต้องมี express.json() และ Header Content-Type ที่ถูกต้องheaders ชื่อเป็น ตัวพิมพ์เล็ก และอ่านด้วย []params, query, headers เป็น string เสมอ
Workshop — ใช้ครบทั้ง 4 ช่องทางในเส้นเดียว
สร้าง POST /debug/:id ที่ตอบกลับข้อมูลจากทั้ง 4 ช่องทางพร้อมกัน
{
"params": { "id": "7" },
"query": { "notify": "true" },
"headers": {
"contentType": "application/json",
"userAgent": "...",
"apiKey": "abc123"
},
"body": { "title": "1984" }
}
ยิงทดสอบด้วย POST /debug/7?notify=true พร้อม Header x-api-key: abc123
และ Body {"title":"1984"}
สร้าง DELETE /books/:id เวอร์ชันใหม่ที่
x-api-key ตรงกับค่าใน .env (เพิ่ม API_KEY=mysecret123){ "error": "ต้องระบุ x-api-key" }{ "error": "API key ไม่ถูกต้อง" }POST /books แต่เปลี่ยนชนิด Body ใน Thunder Client เป็น Text
แทน JSON → req.body ได้อะไร? ทำไม?req.headers["X-API-KEY"] (ตัวพิมพ์ใหญ่) → ได้ค่าไหม? ทำไม??notify=false แล้วเช็กด้วย if (req.query.notify) → เข้าเงื่อนไขไหม? ทำไม?
ข้อ 1. ทำไมห้ามส่งรหัสผ่านผ่าน query string
ข้อ 2. req.headers.content-type เขียนแบบนี้ได้ไหม
ข้อ 3. Client ส่ง Header ชื่อ X-API-KEY มา ต้องอ่านด้วยอะไร
ข้อ 4. if (req.query.notify) เมื่อยิง ?notify=false จะเป็นอย่างไร
ข้อ 5. express.json() จะแกะ body เมื่อไหร่
ข้อ 6. ควรส่ง Token สำหรับยืนยันตัวตนไปทางไหน