تعيين الطلبات
تعيين الطلبات
في الدرس السابق تعلّمت أن @RestController يُعلّم فئةً لتكون نقطة دخول حركة HTTP. لكن المتحكم وحده لا يفعل شيئًا — تحتاج إلى إخبار Spring بأيّ طريقة HTTP وأيّ مسار URL يجب أن يُشغّل كل دالة معالج. هذا هو ما تفعله تعليمات تعيين الطلبات، واختيارها بصورة صحيحة هو أحد أهم قرارات التصميم التي ستتخذها عند بناء واجهة برمجية REST.
أفعال HTTP الأربعة الرئيسية
تُعيّن REST عمليات CRUD على أربع طرق HTTP، لكل منها عقد دلالي مميز:
- GET — استرداد مورد؛ يجب أن يكون آمنًا (بلا آثار جانبية) وإيدمبوتنتًا.
- POST — إنشاء مورد جديد أو تشغيل إجراء؛ ليس إيدمبوتنتًا.
- PUT — استبدال مورد بالكامل؛ إيدمبوتنت (استدعاؤه مرتين يُعطي النتيجة ذاتها).
- DELETE — حذف مورد؛ إيدمبوتنت.
يوفر Spring تعليمًا اختصاريًا مخصصًا لكل فعل. التعليم العام القديم @RequestMapping لا يزال يعمل وهو مفيد حين تحتاج إلى مشاركة مسار أساسي بين جميع الدوال في متحكم، لكن للمعالجات الفردية تكون الاختصارات أنظف وأوضح.
@GetMapping
استخدم @GetMapping لأي عملية تقرأ البيانات دون تعديل حالة الخادم. يمكن تخزين معالج GET المصمم جيدًا في ذاكرة التخزين المؤقت وإعادة المحاولة بأمان واستدعاؤه مرارًا دون قلق من آثار جانبية مكررة.
@RequestMapping على مستوى الفئة هو بادئة. حين تضع @RequestMapping("/api/products") على الفئة، تُضاف كل تعليمة على مستوى الدالة إليها. فـ@GetMapping("/{id}") يُحلَّل إلى GET /api/products/{id}. هذا يُجنّبك تكرار المسار الأساسي في كل دالة.
@PostMapping
POST هو الفعل الصحيح حين تطلب من الخادم إنشاء شيء جديد. النمط المعياري هو: يُرسل العميل جسم JSON ببيانات المورد، يُنشئ الخادم المورد، ويحتوي الرد على المورد المُنشأ حديثًا (أو على أقل تقدير معرّفه وحالة 201 Created).
يُخبر التعليم @RequestBody Spring بإلغاء تسلسل JSON الوارد إلى كائن Java باستخدام Jackson. ستتعمق في Jackson وResponseEntity في دروس لاحقة؛ لكن تذكّر الآن أن POST يجب أن يُرجع 201 Created، لا 200 OK.
@PutMapping
يستبدل PUT المورد المستهدف بالكامل. إذا أرسل العميل كائنًا جزئيًا، فعلى الخادم معالجة الحقول المفقودة باعتبارها قيم فارغة متعمدة (أو الحالة الخالية للمورد). إذا أردت التحديثات الجزئية، استخدم PATCH — لكن هذا يُغطى في درس أفضل الممارسات.
PUT /resources/{id} بإنشاء مورد إن لم يكن موجودًا (upsert). هذا صحيح في REST لكنه يتطلب أن يُوفّر العميل المعرّف. وهو مناسب حين تكون المعرّفات مفاتيح طبيعية (مثل اسم المستخدم). حين تكون المعرّفات من توليد الخادم (مثل المفاتيح التلقائية في قاعدة البيانات)، POST-للإنشاء هو النمط المعياري.
@DeleteMapping
يحذف DELETE المورد المُحدَّد. يُرجع الحذف الناجح تقليديًا 204 No Content — نجحت العملية لكن لا يوجد جسم للإرجاع.
تصميم المسارات — قواعد عملية
بنية URL التي تختارها هي جزء من العقد العام للواجهة البرمجية. تغييرها لاحقًا هو تغيير جذري لكل عميل. هذه القواعد توفّر عليك الكثير من المتاعب:
- استخدم الأسماء لا الأفعال. طريقة HTTP هي الفعل. اكتب
/api/products، لا/api/getProductsأو/api/createProduct. - استخدم الأسماء الجمع للمجموعات.
/api/productsللمجموعة، و/api/products/{id}لعنصر واحد. خلط المفرد والجمع يُربك المستدعين. - عبّر عن العلاقات في المسار. إذا كانت المراجعة تنتمي إلى منتج، فالمسار
/api/products/{productId}/reviewsيُوضّح الملكية. - اجعل المسارات بحروف صغيرة مع شرطات.
/api/product-categories، لا/api/productCategoriesأو/api/ProductCategories. عناوين URL حساسة لحالة الأحرف على معظم الخوادم، وcamelCase يبدو غريبًا فيها. - لا تُصدر نسخة في المسار الآن. الإصدار يُغطى في الدرس التاسع؛ إضافة
/v1/في اليوم الأول بدون استراتيجية يُولّد فوضى.
تحت الغطاء: كيف تُعيَّن التعليمات إلى @RequestMapping
التعليمات الاختصارية هي أغلفة رفيعة. فمثلًا @GetMapping("/path") مكافئة تمامًا لـ @RequestMapping(value = "/path", method = RequestMethod.GET). معرفة هذا يُهمّك حين تحتاج إلى ضبط سمات إضافية مثل produces (التفاوض على المحتوى) أو consumes — يمكنك استخدام الاختصار وإضافة تلك السمات مباشرةً:
IllegalStateException: Ambiguous mapping عند بدء التشغيل. هذا أمر جيد — فهو انتهاك عقد في وقت البدء لا خطأ صامت في وقت التشغيل. صحّحه بتمييز المسارات أو الطرق أو قيود consumes/produces.
الخلاصة
تربط @GetMapping و@PostMapping و@PutMapping و@DeleteMapping دوالَّ معالجيك بأفعال HTTP ومسارات محددة. اجمعها مع @RequestMapping على مستوى الفئة كبادئة للإبقاء على متحكمك نظيفًا. صمّم مساراتك حول أسماء الموارد، استخدم أسماء الجمع للمجموعات، وعبّر عن الملكية في المسارات الفرعية المتداخلة. في الدرس التالي ستتعلم كيفية استخراج أجزاء ديناميكية من تلك المسارات باستخدام @PathVariable و@RequestParam.