หากไม่ปิด additionalProperties การตรวจสอบ Schema ก็ไม่ได้ตรวจอะไร
สรุปสำคัญ: การตรวจสอบผ่านไม่ได้แปลว่าข้อมูลสะอาด Pydantic โดยค่าเริ่มต้นเมินฟิลด์ที่ไม่รู้จัก, Zod ตัดทิ้ง, Ajv ปล่อยผ่าน — พารามิเตอร์ที่โมเดลมั่วและคีย์พิมพ์ผิดลอดผ่านอย่างเงียบงัน บทความนี้แสดงสามรูปแบบของการยอมรับอย่างเงียบและวิธีที่ครบถ้วน: ปิดออบเจ็กต์ ควบคุมการแปลงชนิด และรัดทุกระดับของการซ้อน

สรุป: โดยค่าเริ่มต้น ตัวตรวจสอบจะเมินฟิลด์ที่ไม่ได้ประกาศแทนที่จะปฏิเสธ หากไม่มี additionalProperties: false การผ่านการตรวจสอบพิสูจน์เพียงว่าฟิลด์ที่ประกาศไว้เรียบร้อยดี
พฤติกรรมที่อันตรายที่สุดของตัวตรวจสอบไม่ใช่การโยน error แต่คือความเงียบ คีย์ที่พิมพ์ผิดหรือพารามิเตอร์ที่โมเดลสมมติขึ้นลอดผ่านการตรวจสอบอย่างเงียบงันและระเบิดในเลเยอร์ธุรกิจ
หนึ่ง: การตรวจสอบที่ "ผ่าน"
{
"type": "object",
"properties": {
"order_id": { "type": "string" },
"amount": { "type": "number" }
},
"required": ["order_id", "amount"]
}
ส่ง {"order_id": "A1", "amount": 50, "reasn_code": "damaged"} — คีย์ที่สามพิมพ์ผิด ผลจาก Ajv ค่าเริ่มต้นคือ ผ่าน ฟิลด์เกินไม่ถูกปฏิเสธและไม่ถูกทำเครื่องหมาย
ในข้อกำหนด JSON Schema ค่าเริ่มต้นของ additionalProperties คือ true: ฟิลด์ที่ไม่ได้ประกาศได้รับอนุญาต
สอง: สามรูปแบบของความเงียบ
| Runtime | พฤติกรรมเริ่มต้น | ผลลัพธ์ |
|---|---|---|
| Ajv (JS) | ปล่อยผ่าน | ข้อมูลสกปรกเข้าถึงตรรกะ |
Zod z.object |
ตัดออก | ฟิลด์หายไป ออบเจ็กต์สั้นลง |
| Pydantic v2 | เมิน (extra="ignore") |
เช่นเดียวกัน ไม่มีร่องรอยใน log |
การตัดออกแอบแฝงกว่าการปล่อยผ่าน ทั้งสองไลบรารี ล้มเหลวอย่างเงียบงันในลักษณะเดียวกัน โดยค่าเริ่มต้น
สาม: การเรียกเครื่องมือ LLM ขยายปัญหา
อาร์กิวเมนต์ถูกสร้างโดยโมเดล: มันมั่นใจว่าแต่งชื่อฟิลด์และส่ง "3" แทนตัวเลข แพลตฟอร์มกำหนดพารามิเตอร์เป็น JSON Schema แต่ โดยค่าเริ่มต้นไม่บังคับให้ schema ปิดสนิท — โหมด strict ต้องเปิดอย่างชัดเจน และข้อจำกัดทางไวยากรณ์ครอบคลุมเฉพาะเอาต์พุตตรงของโมเดล
{
"order_id": 4411,
"refund_amount_usd": 50,
"reason_code": "damaged",
"urgent": "yes"
}
เมื่อปิดออบเจ็กต์ reason_code และ urgent จะถูกปฏิเสธ และ error ชี้ไปที่พาธ /reason_code
สี่: วิธีปิด
Ajv: เพิ่ม additionalProperties: false ที่โหนดออบเจ็กต์
import Ajv from "ajv";
const ajv = new Ajv({ allErrors: true });
const schema = {
type: "object",
properties: { order_id: { type: "string" }, amount: { type: "number" } },
required: ["order_id", "amount"],
additionalProperties: false,
};
const validate = ajv.compile(schema);
validate({ order_id: "A1", amount: 50, reasn_code: "x" }); // false
Zod: เปลี่ยน z.object เป็น z.strictObject หรือโยง .strict() Pydantic: ตั้ง ConfigDict(extra="forbid")
กฎร่วม: การปิดทำเป็นรายระดับ ออบเจ็กต์ที่ซ้อนกันทุกตัวต้องมี additionalProperties: false ของตัวเอง
ห้า: นโยบายการแปลงชนิด
โหมด lax ของ Pydantic แปลง "5" เป็น 5 มีสามนโยบาย: เข้มงวด (ปฏิเสธชนิดที่ผิด), ควบคุม (อนุญาตเฉพาะการแปลงที่ชัดเจนและจำกัดขอบเขต), เปิดกว้าง (เฉพาะข้อมูลภายใน) สำหรับอินพุตที่ไม่น่าเชื่อถือ ให้เลือกข้อ 1 หรือ 2 และบันทึกไว้ในการทดสอบ
หก: เช็กลิสต์การซ้อน
| จุด | การกระทำ |
|---|---|
| ออบเจ็กต์ราก | additionalProperties: false |
| ออบเจ็กต์ซ้อนทุกตัว | เช่นเดียวกัน |
| สมาชิกอาร์เรย์ | ปิดออบเจ็กต์ใน items ด้วย |
| enum และช่วง | เพิ่ม enum, minimum/maximum |
| schema ที่สร้างอัตโนมัติ | เติมด้วยมือ |
เจ็ด: หลังถูกปฏิเสธ
allErrors: true รายงานฟิลด์ที่ละเมิดทั้งหมดในครั้งเดียว; verbose: true ให้เส้นทาง ปัญหาไวยากรณ์ควรถูก parser จับก่อนการตรวจสอบ schema
คำถามที่พบบ่อย
เข้มงวดเกินไปหรือไม่?
ขึ้นกับสัญญา สำหรับอินพุตที่คุณกำหนดเอง ควรปิด; สำหรับข้อมูลบุคคลที่สามที่มีส่วนขยาย ให้ปิดเฉพาะแกนและแยกโซนขยาย
การ strip ของ Zod ปลอดภัยกว่าหรือ?
ไม่: มันปกป้องปลายน้ำโดยแลกกับผู้เรียก error ควรปรากฏเร็วและชัดเจน
โหมด strict ของแพลตฟอร์มแทนการตรวจสอบในเครื่องได้หรือ?
ไม่ได้ ตัว schema เองอาจยังเปิดอยู่ และไวยากรณ์อาจไม่ครอบคลุมอาร์กิวเมนต์ของเครื่องมือ
หลังปิดแล้วข้อมูลเก่าพังเป็นวงกว้าง?
นั่นแหละคือสิ่งที่ต้องเปิดเผย ใช้ allErrors สำรวจ เพิ่มฟิลด์ที่ไม่เป็นอันตราย แก้ฟิลด์ที่เป็นอันตราย
ทางแก้หนึ่งบรรทัด?
ไม่มี แต่การกระทำขั้นต่ำ: ตรวจว่าทุก type: "object" ตามด้วย additionalProperties: false