رموز حالة HTTP ودلالات REST
رموز حالة HTTP ودلالات REST
إعادة رمز حالة HTTP الصحيح ليست مسألة جمالية — بل هي آلية الإشارة الأساسية التي تمتلكها واجهة برمجية (API) مع كل عميل وخادم وكيل وذاكرة تخزين مؤقت وأداة مراقبة في السلسلة. يُخبر الرمز المختار بدقة المُستدعيَ ما إذا كان عليه إعادة المحاولة، أو تحليل الجسم، أو إعادة التوجيه، أو تسجيل تنبيه، أو لا يفعل شيئًا. الخطأ في اختيار الرموز يُقوّض الثقة في واجهتك البرمجية ويجبر العملاء على كتابة حلول وقائية. يربط هذا الدرس عائلات الحالة القياسية بعمليات REST الحقيقية ويُظهر كيف يُتيح لك Spring Boot التحكم فيها بدقة.
العائلات الخمس لرموز الحالة
تنقسم رموز حالة HTTP إلى خمس عائلات، لكل منها معنى مشترك:
- 1xx — معلوماتية: الطلب قيد المعالجة. نادرة في REST APIs؛ نادرًا ما ستُرسلها بنفسك.
- 2xx — النجاح: اكتملت العملية على النحو المطلوب. هنا تُركّز معظم جهد التصميم.
- 3xx — إعادة التوجيه: يجب على العميل اتخاذ إجراء آخر، عادةً اتباع عنوان URL جديد.
- 4xx — خطأ العميل: الطلب خاطئ. يجب على العميل تصحيحه قبل إعادة المحاولة.
- 5xx — خطأ الخادم: فشل الخادم. يجوز للعميل إعادة المحاولة لاحقًا.
عائلة 2xx — مطابقة الرمز للعملية
يلجأ كثير من المطورين افتراضيًا إلى 200 OK لكل شيء. هذا يعمل، لكنه يفقد الدقة. الرمز الصحيح:
- 200 OK — نجحت عملية قراءة أو استبدال كامل وجسم الاستجابة يحمل النتيجة. استخدمه لـ
GETوPUT(عند إعادة المورد المُحدَّث) وأفعالPOSTالتي ليست إنشاء موارد. - 201 Created — تم إنشاء مورد جديد. استخدمه لـ
POST(وأحيانًاPUT). يجب أن تتضمن الاستجابة ترويسةLocationتُشير إلى عنوان URL للمورد الجديد. - 204 No Content — نجحت العملية لكن لا يوجد ما يُعاد. استخدمه لـ
DELETEولـPUT/PATCHحين لا تحتاج إلى إعادة التمثيل المُحدَّث. - 202 Accepted — قُبل الطلب للمعالجة غير المتزامنة لكنه لم يكتمل بعد. يحتوي الجسم عادةً على معرّف مهمة أو وظيفة يمكن للعميل استطلاعه.
إعادة 201 وLocation في Spring Boot
استخدم ResponseEntity مع UriComponentsBuilder أو ServletUriComponentsBuilder لبناء عنوان URI للموقع بشكل نظيف:
يُعدّ ResponseEntity.created(uri) دالة مصنع تضبط الحالة على 201 وترويسة Location في استدعاء واحد. إضافة .body(saved) اختيارية — يُغفلها بعض الفرق للإبقاء على الاستجابة موجزة.
إعادة 204 لعملية DELETE
استخدام Void كنوع عام يوضح صراحةً أنه لا يُتوقع أي جسم. ResponseEntity.noContent().build() هي الدالة المصنع المعيارية.
عائلة 4xx — أخطاء العميل
تُخبر هذه الرموز العميل بأنه يجب تصحيح الطلب. لا تدمجها كلها في 400:
- 400 Bad Request — جسم الطلب أو المعاملات غير صالحة تركيبيًا أو دلاليًا. استخدمه عند فشل التحقق من الصحة.
- 401 Unauthorized — العميل غير مُوثَّق. الاسم مُضلّل؛ معناه الحقيقي "غير مُعرَّف الهوية". يضبط Spring Security هذا تلقائيًا للنقاط المحمية.
- 403 Forbidden — العميل مُوثَّق لكن ليس لديه إذن لهذه العملية.
- 404 Not Found — المورد المطلوب غير موجود. لا تستخدم 200 مع جسم فارغ عندما يكون السجل مفقودًا.
- 405 Method Not Allowed — طريقة HTTP غير مدعومة لهذا المسار (يضبطه Spring تلقائيًا).
- 409 Conflict — لا يمكن إتمام العملية بسبب تعارض مع الحالة الحالية للمورد، مثل بريد إلكتروني مكرر أثناء التسجيل.
- 422 Unprocessable Entity — التركيب صحيح لكن قواعد العمل ترفض البيانات. تُفضّل كثير من الفرق هذا على 400 لأخطاء التحقق للتمييز بين فشل التحليل وفشل المنطق.
- 429 Too Many Requests — تجاوز العميل حد معدل الطلبات.
رمي الاستثناءات التي تُعيَّن إلى رموز الحالة
إزعاج كل دالة في المتحكم بقرارات رمز الحالة أمر مُزعج. النمط الأنظف هو رمي استثناء مخصص للنطاق والسماح لـ @ControllerAdvice المركزي بتعيينه:
يُنفّذ ProblemDetail (المُقدَّم في Spring 6 / Spring Boot 3) معيار RFC 9457 (المعروف سابقًا بـ RFC 7807)، وهو صيغة تفاصيل المشكلة القياسية. يتلقى العملاء جسم JSON يحتوي على حقول type وtitle وstatus وdetail يمكنهم معالجتها برمجيًا.
استخدام @ResponseStatus على فئات الاستثناء
في الحالات البسيطة يمكنك تعليق الاستثناء مباشرةً بدلًا من كتابة @ExceptionHandler:
سيضبط Spring رمز الحالة تلقائيًا عند انتشار هذا الاستثناء من المتحكم. المقايضة: لا يمكنك تخصيص جسم الاستجابة بهذه الطريقة، لذا فضّل @ExceptionHandler + ProblemDetail لأي واجهة برمجية في الإنتاج يحتاج عملاؤها إلى تفاصيل خطأ منظّمة.
عائلة 5xx — متى تستخدم 500 مقابل 503
تجنّب كتابة كود يرمي 500 عمدًا. دع الاستثناءات غير المعالجة تتدفق إلى معالج Spring الافتراضي الذي يُعيد 500. استخدم 503 Service Unavailable عندما يكون اعتماد خارجي (قاعدة بيانات، خدمة خارجية) متوقفًا مؤقتًا وتريد الإشارة إلى أن على العميل إعادة المحاولة — مع تضمين ترويسة Retry-After اختياريًا. تأتي 502 Bad Gateway و504 Gateway Timeout من طبقات البنية التحتية (الوكلاء العكسيون وموازنات الحمل)، وليس من كود تطبيقك.
{"success": false, "error": "not found"} ملفوفة في استجابة 200 تُعطّل كل أداة مدركة لـ HTTP: ذاكرات التخزين المؤقت تحفظ الخطأ، والمراقبة تُصنّفه نجاحًا، ومنطق إعادة المحاولة يتجاهله. استخدم رمز الحالة الصحيح لتعمل طبقة البروتوكول لصالحك.
دلالات REST: القدرة على الاستيعاب والسلامة
لا تحكي رموز الحالة القصة كاملة. تُشكّل خاصيتان من HTTP الرموز المناسبة:
- الأساليب الآمنة (
GET،HEAD) يجب ألا تغيّر حالة الخادم. لا ينبغي أن تُعيد أبدًا201أو204. - الأساليب القابلة للاستيعاب (Idempotent) (
GET،PUT،DELETE،HEAD،OPTIONS) تُنتج النتيجة ذاتها بغض النظر عن عدد مرات الاستدعاء. يجب أن تُعيد عمليةDELETEعلى مورد لم يعد موجودًا404في المرة الأولى و404مجددًا عند التكرار — وليس رمزًا مختلفًا أو خطأ حول "تم الحذف بالفعل". POSTليست آمنة ولا قابلة للاستيعاب. استدعاءPOST /productsمرتين يجب أن ينشئ منتجَين، ويُعيد الاستدعاء الثاني201مجددًا (أو409إذا كانت التكرارات محظورة).
جدول مرجعي سريع
GET /resources— 200 OK (قائمة)، 404 مستحيلة (القائمة فارغة = 200 +[])GET /resources/{id}— 200 OK أو 404 Not FoundPOST /resources— 201 Created + Location، أو 400/409 عند الخطأPUT /resources/{id}— 200 OK (مع جسم) أو 204 No Content (بدون جسم)؛ 404 إذا كان مفقودًاPATCH /resources/{id}— 200 OK أو 204 No Content؛ 404 أو 422 عند الخطأDELETE /resources/{id}— 204 No Content؛ 404 إذا كان مفقودًا
الخلاصة
اختيار رمز الحالة الصحيح فعل تصميمي متعمد. أعد 201 مع ترويسة Location عند الإنشاء، و204 عند الحذف أو التحديث دون إعادة الجسم، و404 عند غياب المورد، والرمز المناسب من عائلة 4xx عندما يُرسل العميل بيانات خاطئة. مركز قرارات رموز الحالة في @RestControllerAdvice باستخدام ProblemDetail، ولا تسيء أبدًا استخدام 200 لإخفاء الأخطاء. بهذه العادات تتواصل واجهتك البرمجية بدقة عبر HTTP باللغة ذاتها التي يتحدثها كل وكيل وعميل وأداة مراقبة.