بدون additionalProperties، لا يتحقق تحقق المخطط من أي شيء
الخلاصة: اجتياز التحقق لا يعني بيانات نظيفة. Pydantic يتجاهل الحقول غير المعروفة افتراضيًا، وZod يزيلها، وAjv يمررها — المعاملات المتخيلة والمفاتيح المكتوبة خطأً تعبر بصمت. يعرض هذا المقال ثلاث صور للقبول الصامت ويقدم طرقًا كاملة: إغلاق الكائنات، والتحكم في تحويل الأنواع، وشد كل مستوى تداخل.

الخلاصة: افتراضيًا، تتجاهل أدوات التحقق الحقول غير المعلنة بدلًا من رفضها. بدون additionalProperties: false، النجاح في التحقق يثبت فقط أن الحقول المعلنة سليمة.
أخطر سلوك لأداة التحقق ليس إطلاق خطأ، بل الصمت. مفتاح مكتوب خطأً أو معامل اختلقه النموذج يعبر التحقق بلا صوت ولا ينفجر إلا في منطق العمل.
واحد: تحقق «ينجح»
{
"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: الحقول غير المعلنة مسموحة.
اثنان: ثلاث صور للصمت
| بيئة التشغيل | السلوك الافتراضي | النتيجة |
|---|---|---|
| Ajv (JS) | يمررها | بيانات وسخة تصل إلى المنطق |
Zod z.object |
يزيلها | الحقل يختفي ويقصر الكائن |
| Pydantic v2 | يتجاهلها (extra="ignore") |
مثلها، بلا أثر في السجلات |
الإزالة أخفى من التمرير. كلتا المكتبتين تفشلان افتراضيًا بالطريقة نفسها — بصمت.
ثلاثة: استدعاء أدوات LLM يضخّم المشكلة
المعاملات يولدها النموذج: يخترع أسماء الحقول بثقة ويمرر "3" مكان الرقم. تحدد المنصات المعاملات عبر JSON Schema لكنها لا تفرض إغلاق المخطط افتراضيًا — يجب تفعيل وضع strict صراحةً، والقيود النحوية تغطي مخرجات النموذج المباشرة فقط.
{
"order_id": 4411,
"refund_amount_usd": 50,
"reason_code": "damaged",
"urgent": "yes"
}
مع إغلاق الكائن، يُرفض reason_code وurgent ويشير الخطأ إلى المسار /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 خاصًا به.
خمسة: سياسة تحويل الأنواع
يحوّل الوضع المتساهل في Pydantic القيمة "5" إلى 5. ثلاث سياسات: صارمة (رفض الأنواع الخاطئة)، مضبوطة (تحويلات صريحة محدودة فقط)، حرة (بيانات داخلية فقط). للمدخلات غير الموثوقة اختر الأولى أو الثانية وسجّلها في الاختبارات.
ستة: قائمة فحص التداخل
| النقطة | الإجراء |
|---|---|
| الكائن الجذر | additionalProperties: false |
| كل كائن متداخل | مثلها |
| عناصر المصفوفات | أغلق الكائنات داخل items |
| التعداد والنطاقات | أضف enum وminimum/maximum |
| المخططات المولدة | أكملها يدويًا |
سبعة: بعد الرفض
allErrors: true يبلّغ عن كل الحقول المخالفة دفعة واحدة؛ وverbose: true يعطي المسارات. مشكلات الصياغة يمسكها المحلل قبل التحقق من المخطط.
أسئلة شائعة
أليس هذا صارمًا أكثر من اللازم؟
يتوقف على العقد. للمدخلات التي تعرّفها بنفسك، أغلق؛ ولبيانات أطراف ثالثة قابلة للتوسعة، أغلق النواة فقط وعزل منطقة الامتداد.
الإزالة في Zod ليست أكثر أمانًا؟
لا: تحمي المصب على حساب المستدعي. ينبغي أن تظهر الأخطاء مبكرًا وبوضوح.
هل وضع strict في المنصة يغني عن التحقق المحلي؟
لا. المخطط نفسه قد يكون مفتوحًا، والقواعد النحوية قد لا تغطي معاملات الأدوات.
بعد الإغلاق تفشل البيانات القديمة جماعيًا؟
هذا بالضبط ما كان يجب كشفه. جرد عبر allErrors، أضف الحقول غير الضارة، وأصلح الضارة.
حل بسطر واحد؟
لا، لكن أصغر إجراء: تحقق أن كل type: "object" يليه additionalProperties: false.