استجابات الأخطاء في واجهة برمجة التطبيقات (API) الخاصة بك هي جزء من عقدها. يقوم العملاء بتحليلها، وتعتمد منطق إعادة المحاولة عليها، ويبحث مهندسو الدعم عنها في الساعة الثانية صباحًا. ومع ذلك، تصمم معظم الفرق المسار السعيد بالتفصيل وتترك الأخطاء تظهر وفقًا لما يفعله الإطار الافتراضي. هكذا ينتهي بك المطاف بثلاثة أشكال مختلفة للأخطاء في واجهة برمجة تطبيقات واحدة، واستجابة 200 تحتوي على "success": false، وتتبع مكدس (stack trace) يسرب مخطط قاعدة بياناتك إلى الإنترنت العام.
يغطي هذا الدليل أفضل ممارسات التعامل مع أخطاء واجهة برمجة التطبيقات (API) لخدمات REST من البداية إلى النهاية: اختيار رمز الحالة الصحيح، توحيد هيكل استجابة الخطأ باستخدام RFC 9457 Problem Details، فصل الرموز القابلة للقراءة آليًا عن الرسائل البشرية، تحديد الأخطاء القابلة لإعادة المحاولة، وإبقاء الأسرار بعيدًا عن الاستجابات. يستند هذا إلى تحليلنا لـ رموز حالة HTTP التي يجب أن تستخدمها واجهات برمجة تطبيقات REST ويضيف القرارات على مستوى العقد التي يتركها الدليل مفتوحة. سترى أيضًا كيفية اختبار كل مسار فشل في Apidog، لأن عقد الأخطاء الذي لا تختبره أبدًا هو عقد لا تملكه.
ابدأ برمز الحالة، وليس بهيكل الاستجابة
يوفر لك بروتوكول HTTP بالفعل طبقة أولى من دلالات الأخطاء مجانًا. يحدد RFC 9110 عائلات رموز الحالة: تشير 4xx إلى أن العميل ارتكب خطأ وأن تكرار نفس الطلب سيفشل مرة أخرى؛ وتشير 5xx إلى فشل الخادم وقد يكون طلب العميل سليمًا. احصل على هذا التقسيم بشكل صحيح قبل أن تكتب سطرًا واحدًا من نص الخطأ، لأن العملاء العامين، والوكلاء (proxies)، وذاكرات التخزين المؤقت (caches)، ومكتبات إعادة المحاولة كلها تتفرع بناءً عليه دون قراءة ملف JSON الخاص بك أبدًا.
تتجمع الأخطاء الأكثر شيوعًا حول عدد قليل من الأزواج المتشابهة. اجعل مرجع رموز حالة HTTP الخاص بـ MDN مفتوحًا أثناء التصميم، واستخدم جدول القرارات هذا للرموز التي تتسبب في إرباك الفرق.
| الحالة | استخدم | لا | السبب |
|---|---|---|---|
| طلب غير سليم: JSON معطل، نوع محتوى خاطئ، حقل إلزامي مفقود | 400 طلب سيء (Bad Request) | 422 | لا يمكن للخادم تحليل الطلب أو فهمه على الإطلاق |
| طلب سليم ينتهك القواعد الدلالية: المبلغ سالب، العملة غير مدعومة | 422 محتوى غير قابل للمعالجة (Unprocessable Content) | 400 | بناء الجملة سليم؛ القيم ليست كذلك |
| لا توجد بيانات اعتماد، أو رمز مميز منتهي الصلاحية/غير صالح | 401 غير مصرح به (Unauthorized) | 403 | لم يثبت العميل هويته. أرسل WWW-Authenticate |
| بيانات اعتماد صالحة، أذونات غير كافية | 403 محظور (Forbidden) | 401 | الهوية معروفة؛ والوصول مرفوض. إعادة المصادقة لن تجدي نفعًا |
| المورد لم يوجد أبدًا، أو لن تؤكد وجوده | 404 غير موجود (Not Found) | 410 | الافتراضي الآمن؛ يخفي الموارد أيضًا من الاستكشاف غير المصرح به |
| المورد موجود وتمت إزالته عمدًا وبشكل دائم | 410 زال (Gone) | 404 | يخبر العملاء وبرامج الزحف بحذف إشاراتهم المرجعية |
| تعارض في الحالة: مفتاح مكرر، إصدار قديم، تعارض في التعديل | 409 تعارض (Conflict) | 400 | الطلب صالح ولكنه يتعارض مع الحالة الحالية للمورد |
| تجاوز العميل حد المعدل | 429 طلبات كثيرة جدًا (Too Many Requests) | 503 | قم دائمًا بتضمين Retry-After حتى يتراجع العملاء بشكل صحيح |
| استثناء غير معالج في التعليمات البرمجية الخاصة بك | 500 خطأ داخلي بالخادم (Internal Server Error) | 502 | تعطل الخادم الخاص بك |
| خدمة المنبع أعادت بيانات خاطئة إلى بوابتك | 502 بوابة غير صالحة (Bad Gateway) | 500 | الفشل يقع بعد الحافة، وليس فيها |
| الخادم مثقل أو قيد الصيانة | 503 خدمة غير متاحة (Service Unavailable) | 500 | مؤقتة حسب التعريف؛ أضف Retry-After عندما تستطيع |
| انتهت مهلة خدمة المنبع | 504 انتهاء مهلة البوابة (Gateway Timeout) | 500 | يميز "الاعتماد البطيء" عن "التعليمات البرمجية المعطلة" |
يستحق اثنان من هذه النقاط تركيزًا إضافيًا. أولاً، يعتبر 401 مقابل 403 حدًا أمنيًا، وليس اختيارًا تصميميًا: إعادة 403 إلى متصل غير مصادق عليه يسرب حقيقة وجود المورد. ثانيًا، 429 بدون Retry-After يدرب العملاء على إرسال طلبات متكررة بسرعة. إذا كنت تحدد معدل الطلبات، ويجب أن تفعل ذلك، فقم بإقران الحالة بإشارة تراجع محددة؛ يغطي دليلنا حول تحديد معدل واجهة برمجة التطبيقات (API rate limiting) حسابات الرأس (header math) والخوارزميات الكامنة وراءها.
شكل واحد لهيكل استجابة الخطأ: تفاصيل المشكلة (RFC 9457 Problem Details)
بمجرد أن يكون رمز الحالة صحيحًا، يجب أن تشارك كل استجابة خطأ من واجهة برمجة التطبيقات الخاصة بك نوع وسائط واحد ومخططًا واحدًا. الإجابة القياسية هي RFC 9457 Problem Details، تُقدم كـ application/problem+json. تحدد خمسة أعضاء أساسيين: type (URI يحدد فئة الخطأ)، title (ملخص بشري قصير)، status (رمز HTTP، مكرر للراحة)، detail (ما الخطأ الذي حدث في هذه الحالة)، و instance (URI لهذا الفشل المحدد). أي شيء آخر يوضع في أعضاء التوسيع التي تحددها بنفسك.
لن نستعرض المواصفات هنا مرة أخرى؛ يشرح شرحنا لـ RFC 9457 كل عضو، وقواعد السجل، وكيف يحل محل RFC 7807. ما يهم لعقدك هو النمط: غلاف قياسي، امتدادات مخصصة. إليك فشل التحقق من الصحة على نقطة نهاية المدفوعات.
POST /v1/payments HTTP/1.1
Content-Type: application/json
{ "amount": -1400, "currency": "USD", "source": "card_8xKt2" }
HTTP/1.1 422 Unprocessable Content
Content-Type: application/problem+json
{
"type": "https://api.example.com/problems/validation-error",
"title": "Request validation failed",
"status": 422,
"detail": "One or more fields failed validation.",
"instance": "/v1/payments/requests/req_9f3c1a7b",
"code": "PAYMENT_VALIDATION_FAILED",
"errors": [
{
"field": "amount",
"code": "AMOUNT_NOT_POSITIVE",
"message": "amount must be a positive integer in minor units"
}
],
"request_id": "req_9f3c1a7b"
}
المصفوفة errors[] هي عضو توسيع، وهي الأكثر تفضيلاً للعملاء: تتيح للواجهة الأمامية ربط كل فشل بحقل النموذج الدقيق بدلاً من عرض لافتة عامة غامضة. احتفظ بمسارات الحقول بتنسيق مستقر (JSON Pointer أو مسارات نقطية، اختر واحدًا) حتى تتمكن شيفرة العميل من ربطها برمجياً.
قاعدة واحدة توفر عليك الكثير من المتاعب: أعد هذا الشكل لكل خطأ، بما في ذلك تلك التي يولدها إطار عملك أو بوابتك. العميل الذي يتلقى Problem Details من معالجاتك ولكن HTML من صفحة 502 لموازن التحميل الخاص بك لا يزال عليه كتابة محللين (parsers) اثنين.
الرموز القابلة للقراءة آليًا مقابل الرسائل البشرية
لاحظ أن المثال يتضمن حقلي code و message. هذا مقصود. إنهما يخدمان جماهير مختلفة ولا ينبغي أبدًا دمجهما في سلسلة نصية واحدة.
الرموز القابلة للقراءة آليًا (AMOUNT_NOT_POSITIVE، CURRENCY_UNSUPPORTED، IDEMPOTENCY_KEY_REUSED) هي جزء من العقد. يعتمد العملاء عليها، لذا يجب أن تكون مستقرة، موثقة، وقابلة للعد. لا تجعل العملاء يقومون بتحليل النصوص؛ ففي اللحظة التي يكتب فيها أحدهم if (message.includes("positive"))، يصبح تعديلك للنص تغييرًا قد يؤدي إلى كسر (breaking change).
الرسائل البشرية هي العكس: حرة في التحسين في أي وقت، مكتوبة لمطور يقرأ السجلات، ولا تعتمد عليها الوظائف الأساسية. اذكر ما فشل وكيف يبدو إصلاحه: "يجب أن يكون المبلغ عددًا صحيحًا موجبًا بوحدات صغيرة" أفضل من "مبلغ غير صالح". إذا قمت بالترجمة، فترجم الرسالة واترك الرمز كما هو.
يزداد أهمية هذا الفصل الآن بعد أن أصبح مستهلكو واجهة برمجة التطبيقات يشملون الوكلاء المستقلين. يتعافى العملاء المعتمدون على نماذج اللغة الكبيرة (LLM) بشكل أفضل بكثير من الأخطاء المنظمة والواصفة لذاتها؛ نغطي هذه الزاوية في تصميم أخطاء واجهة برمجة التطبيقات (API) لوكلاء الذكاء الاصطناعي.
ما لا يجب أن يظهر أبدًا في استجابة الخطأ
تعد استجابات الأخطاء قناة استطلاع مفضلة للمهاجمين، لأن حالات الفشل غير المعالجة غالبًا ما تكون مطولة. يجب أن يضمن برمجياتك الوسيطة للأخطاء (error middleware) ألا يصل أي مما يلي إلى العميل أبدًا:
- تتبعات المكدس (Stack traces)، أسماء الفئات، أو مسارات الملفات
- SQL الخام، أجزاء الاستعلام، أو أخطاء ORM
- أسماء المضيفين الداخلية، عناوين IP، المنافذ، أو أسماء الخدمات
- إصدارات المكتبات وسلاسل لافتات الإطار (framework banner strings)
- الأسرار، الرموز المميزة، أو سلاسل الاتصال المضمنة في نص الاستثناء
- ما إذا كان حساب المستخدم موجودًا (في تدفقات تسجيل الدخول وإعادة تعيين كلمة المرور، حافظ على تماثل الفشل)
النمط بسيط: قم باعتراض كل شيء عند الحدود، سجل الاستثناء الكامل من جانب الخادم بمعرف طلب (request ID)، وأعد هيكل Problem Details عامًا بنفس المعرف. يتلقى العميل "detail": "حدث خطأ داخلي", "request_id": "req_51ad0"، وتحصل سجلاتك على الحقيقة، ويمكن للدعم ربط الاثنين.
تحديد الأخطاء القابلة لإعادة المحاولة أو النهائية
كل خطأ تعيده يجيب على سؤال سيسأله العميل: هل يجب أن أحاول هذا مرة أخرى؟ ادمج الإجابة في العقد بدلاً من ترك كل فريق عميل يخمن.
تحمل رموز الحالة دلالات افتراضية. يمكن إعادة محاولة 429، 502، 503، و 504 مع تراجع أسي واهتزاز. الرمز 500 غامض ولكنه عادة ما يستحق محاولة إعادة حذرة واحدة. جميع رموز 4xx الأخرى تقريبًا هي نهائية: إعادة محاولة 401، 403، 404، أو 422 بنفس الطلب يهدر الحصة ويلوث السجلات. تستحق مهلات الانتظار عناية خاصة، حيث قد يكون الطلب قد نجح بعد أن استسلم العميل؛ هذه هي مشكلة انتهاء مهلة الطلب الكلاسيكية 408، ولهذا السبب يجب أن تقبل نقاط النهاية التي تعدل الحالة مفاتيح الثبات (idempotency keys) حتى لا يتم تحصيل دفعة معاد محاولتها مرتين.
يمكنك أيضًا جعل إمكانية إعادة المحاولة صريحة باستخدام عضو توسيع:
{
"type": "https://api.example.com/problems/rate-limited",
"title": "Too many requests",
"status": 429,
"code": "RATE_LIMITED",
"retryable": true,
"retry_after_seconds": 30
}
تسمح لك علامة retryable الصريحة بتجاوز الإعدادات الافتراضية عند الحاجة، مثل تحديد رمز فرعي معين من 500 كنهائي لأن إعادة محاولته يفسد الحالة. وثّق العلامة مرة واحدة وستحصل كل حزمة تطوير برمجيات للعميل (SDK) تشحنها على سلوك تراجع موحد.
معرفات الارتباط وتحديد إصدار عقد الأخطاء
قراران أصغر يكملان العقد، وكلاهما رخيص الآن، ومكلف لاحقًا.
امنح كل طلب معرفًا. اقبل رأس X-Request-Id الوارد (أو قم بإنشاء واحد)، اطبعه على كل سطر سجل، وأعده في كل هيكل خطأ كـ request_id. عندما يلصق العميل خطأ في تذكرة دعم، يحول هذا الحقل الواحد ساعة من البحث في السجلات إلى استعلام واحد. في الإعدادات الموزعة، قم بنشر traceparent الخاص بـ W3C بجانبه حتى يتبع المعرف الطلب عبر الخدمات.
قم بتحديد إصدار عقد الأخطاء الخاص بك مثل واجهة برمجة التطبيقات نفسها. إضافة عضو توسيع جديد أو رمز خطأ جديد آمن. إعادة تسمية errors[].field، أو تغيير معنى رمز، أو الانتقال من شكل مخصص إلى Problem Details هو تغيير جذري (breaking change)، ويكسر مسارات التعليمات البرمجية التي تختبرها الفرق بأقل قدر. يمنحك URI الخاص بـ type آلية نظيفة: حافظ على عناوين URI القديمة للنوع مستقرة إلى الأبد، وقدم عناوين جديدة للدلالات الجديدة، واذكر في وثائقك أن أعضاء التوسيع غير المعروفة والرموز غير المعروفة يجب تجاهلها، وعدم اعتبارها إخفاقات. هذا البند المتعلق بالتوافق الأمامي هو ما يسمح لك بالتطور بدون إصدار ثانٍ.
اختبر كل مسار خطأ في Apidog
إليك الحقيقة غير المريحة: عقود الأخطاء تتدهور لأنه لا يوجد ما يمارسها. المسار السعيد يعمل في كل عرض توضيحي؛ فرع 422 يعمل عندما يصادفه عميل. الحل هو جعل حالات الفشل مواطنين من الدرجة الأولى في مجموعة اختباراتك، وهذا هو المكان الذي يكسب فيه Apidog مكانه في سير العمل.
تتناسب ميزتان مباشرة مع هذه المشكلة.
سيناريوهات الاختبار لجانب الخادم. لكل نقطة نهاية، قم ببناء سيناريو لكل حالة فشل: عدم وجود مصادقة يتوقع 401، دور غير كافٍ يتوقع 403، مبلغ سالب يتوقع 422 مع errors[0].code يساوي AMOUNT_NOT_POSITIVE، حركة مرور مكثفة تتوقع 429 مع رأس Retry-After. تتحقق تأكيدات Apidog المرئية من الحالة، والرؤوس، وحقول هيكل الاستجابة بدون برمجة نصية، ويمكنك التحقق من صحة الحمولة الكاملة مقابل مخطط JSON لتفاصيل المشكلة (Problem Details JSON Schema) الخاص بك بحيث يؤدي أي انحراف في شكل الخطأ إلى فشل التكامل المستمر (CI)، وليس الإنتاج. يوضح دليل تأكيدات واجهة برمجة التطبيقات (API assertions) أنماط التأكيد بالتفصيل.
خوادم وهمية لجانب العميل. تحتاج فرق الواجهة الأمامية وSDK الخاصة بك إلى البناء مقابل استجابات 4xx و 5xx قبل أن يتمكن الواجهة الخلفية من إنتاجها عند الطلب. تعيد خوادم Apidog الوهمية هياكل Problem Details الدقيقة من مواصفات واجهة برمجة التطبيقات الخاصة بك، بحيث يمكنك محاكاة 503 مع Retry-After: 120، أو 409 عند إرسال مزدوج، أو حمولة تحقق كاملة errors[]، ثم مشاهدة كيفية عرض العميل وإعادة المحاولة. لا يوجد قوالب Express مصممة يدويًا، ولا تعليقات على كود الواجهة الخلفية لإجبار الفشل.
صمم عقد الأخطاء، وقم بترميزه كسيناريوهات ومحاكيات، وربط كليهما بنظام التكامل المستمر (CI). قم بتنزيل Apidog وجربه مجانًا؛ استيراد مواصفات OpenAPI موجودة يمنحك استجابات أخطاء قابلة للمحاكاة في دقائق معدودة.
زر
الأسئلة الشائعة
هل يجب أن أستخدم 400 أو 422 لأخطاء التحقق من الصحة؟
استخدم 400 عندما يكون الطلب غير سليم ولا يستطيع الخادم فهمه: JSON غير صالح، نوع محتوى خاطئ، حقل إلزامي مفقود. استخدم 422 عندما يتم تحليل الطلب بشكل سليم ولكن القيم تخالف قواعد نطاقك، مثل مبلغ دفع سالب أو عملة غير مدعومة. الفائدة العملية هي التشخيص: 422 يخبر العميل "أصلح بياناتك"، بينما 400 يقول "أصلح تنسيق طلبك". أيًا كان التقسيم الذي تختاره، طبقه بشكل متسق عبر كل نقطة نهاية.
ما هو application/problem+json؟
هو نوع الوسائط المحدد بواسطة RFC 9457 لتفاصيل المشكلة (Problem Details)، وهو تنسيق أخطاء JSON القياسي لواجهات برمجة تطبيقات HTTP. تحمل استجابة بهذا النوع من المحتوى الأعضاء type، title، status، detail، و instance، بالإضافة إلى أي امتدادات تحددها، مثل مصفوفة errors[] لفشل التحقق من الصحة على مستوى الحقل. يتيح استخدام نوع الوسائط المسجل للعملاء العامين والبرمجيات الوسيطة التعرف على أخطائك بدون تكوين مخصص. يغطي شرحنا لـ RFC 9457 المواصفات بالكامل.
أي أخطاء HTTP يجب أن يعيد العملاء محاولتها تلقائيًا؟
أعد محاولة 429، 502، 503، و 504 مع تراجع أسي واهتزاز، مع احترام Retry-After عند وجوده. تعامل مع 500 على أنه يستحق محاولة إعادة حذرة واحدة. لا تعد محاولة استجابات 4xx الأخرى؛ سيفشل الطلب بنفس الطريقة في كل مرة. لنقاط النهاية التي تعدل البيانات (mutating endpoints)، قم بإقران إعادة المحاولات بمفاتيح الثبات (idempotency keys) حتى لا يتم تحصيل طلب معاد مرتين أو إنشاء موردين متطابقين.
كيف يمكنني اختبار استجابات أخطاء واجهة برمجة التطبيقات دون تعطيل الواجهة الخلفية؟
قم بمحاكاتها. وجه عميلك إلى خادم Apidog الوهمي الذي يعيد هياكل 4xx و 5xx الدقيقة من مواصفاتك، ثم تحقق من سلوك العرض وإعادة المحاولة مقابل كل منها. على جانب الخادم، اكتب سيناريوهات اختبار ترسل حمولات غير صالحة، ومصادقة مفقودة، وحركة مرور مكثفة، ثم تأكد من رموز الحالة، والرؤوس، ومخطط هيكل الخطأ. يعمل كلا الجزأين في التكامل المستمر (CI)، لذا يظل عقد الأخطاء سليمًا دون الحاجة إلى إجبار الأخطاء يدويًا.
