تطوير واجهات REST API

HATEOAS وواجهات برمجة التطبيقات التشعبية

20 دقيقة الدرس 16 من 35

مقدمة إلى HATEOAS

HATEOAS (الوسائط التشعبية كمحرك لحالة التطبيق) هو قيد من قيود بنية تطبيقات REST يميزها عن بنى الشبكة الأخرى. المبدأ هو أن العميل يتفاعل مع تطبيق الشبكة بالكامل من خلال الوسائط التشعبية المقدمة ديناميكيًا بواسطة خوادم التطبيق. لا يحتاج عميل REST إلى معرفة مسبقة حول كيفية التفاعل مع التطبيق أو الخادم بخلاف الفهم العام للوسائط التشعبية.

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

لماذا HATEOAS مهم

يوفر HATEOAS عدة مزايا مهمة لتصميم واجهة برمجة التطبيقات:

  • القابلية للاكتشاف: يمكن للعملاء التنقل في واجهة برمجة التطبيقات باتباع الروابط دون الحاجة إلى توثيق لكل نقطة نهاية
  • المرونة: يمكن تغيير عناوين URL للخادم دون كسر العملاء، طالما ظلت علاقات الروابط متسقة
  • التوثيق الذاتي: تخبر استجابة واجهة برمجة التطبيقات العملاء بالإجراءات المتاحة
  • تقليل الاقتران: يعتمد العملاء على علاقات الروابط بدلاً من عناوين URL المشفرة
  • إدارة الحالة: يمكن للروابط الإشارة إلى انتقالات الحالة المتاحة

فهم علاقات الروابط

تحدد علاقات الروابط العلاقة بين المورد الحالي والمورد المرتبط. تشمل علاقات الروابط الشائعة:

<?php // علاقات الروابط القياسية من IANA $linkRelations = [ \047self\047 => \047رابط إلى المورد نفسه\047, \047next\047 => \047رابط إلى المورد التالي في مجموعة\047, \047prev\047 => \047رابط إلى المورد السابق\047, \047first\047 => \047رابط إلى أول مورد في مجموعة\047, \047last\047 => \047رابط إلى آخر مورد\047, \047edit\047 => \047رابط لتحرير المورد\047, \047delete\047 => \047رابط لحذف المورد\047, \047collection\047 => \047رابط إلى المجموعة الأم\047, \047related\047 => \047رابط إلى مورد ذي صلة\047, ]; // علاقات مخصصة خاصة بالتطبيق $customRelations = [ \047author\047 => \047رابط إلى مؤلف المورد\047, \047comments\047 => \047رابط إلى التعليقات على المورد\047, \047publish\047 => \047إجراء لنشر المورد\047, \047approve\047 => \047إجراء للموافقة على المورد\047, ];

تطبيق HATEOAS الأساسي

لنبدأ بتطبيق بسيط يضيف روابط إلى استجابة المورد:

<?php namespace App\Http\Resources; use Illuminate\Http\Resources\Json\JsonResource; class PostResource extends JsonResource { public function toArray($request) { return [ \047id\047 => $this->id, \047title\047 => $this->title, \047content\047 => $this->content, \047author\047 => $this->author->name, \047created_at\047 => $this->created_at->toIso8601String(), \047links\047 => [ \047self\047 => [ \047href\047 => route(\047api.posts.show\047, $this->id), \047method\047 => \047GET\047, ], \047update\047 => [ \047href\047 => route(\047api.posts.update\047, $this->id), \047method\047 => \047PUT\047, ], \047delete\047 => [ \047href\047 => route(\047api.posts.destroy\047, $this->id), \047method\047 => \047DELETE\047, ], \047author\047 => [ \047href\047 => route(\047api.users.show\047, $this->author_id), \047method\047 => \047GET\047, ], \047comments\047 => [ \047href\047 => route(\047api.posts.comments.index\047, $this->id), \047method\047 => \047GET\047, ], ], ]; } }

ينتج هذا استجابة مثل:

{ "id": 42, "title": "فهم HATEOAS", "content": "HATEOAS مفهوم قوي...", "author": "أحمد محمد", "created_at": "2026-02-14T10:30:00Z", "links": { "self": { "href": "https://api.example.com/posts/42", "method": "GET" }, "update": { "href": "https://api.example.com/posts/42", "method": "PUT" }, "delete": { "href": "https://api.example.com/posts/42", "method": "DELETE" }, "author": { "href": "https://api.example.com/users/10", "method": "GET" }, "comments": { "href": "https://api.example.com/posts/42/comments", "method": "GET" } } }

فهم HAL (لغة التطبيق النصي التشعبي)

HAL هو تنسيق بسيط يوفر طريقة متسقة وسهلة للربط التشعبي بين الموارد في واجهة برمجة التطبيقات. تم إنشاؤه لتوحيد تطبيق HATEOAS ويتضمن عنصرين رئيسيين: الموارد والروابط.

بنية HAL: يستخدم HAL _links لعناصر التحكم في الوسائط التشعبية و _embedded للموارد المتداخلة، مما يجعل البنية قابلة للتنبؤ وسهلة التحليل.

تطبيق تنسيق HAL

إليك كيفية تطبيق تنسيق HAL في Laravel:

<?php namespace App\Http\Resources; use Illuminate\Http\Resources\Json\JsonResource; class HalPostResource extends JsonResource { public function toArray($request) { return [ \047id\047 => $this->id, \047title\047 => $this->title, \047content\047 => $this->content, \047status\047 => $this->status, \047published_at\047 => $this->published_at, // قسم HAL _links \047_links\047 => [ \047self\047 => [ \047href\047 => route(\047api.posts.show\047, $this->id), ], \047author\047 => [ \047href\047 => route(\047api.users.show\047, $this->author_id), \047title\047 => $this->author->name, ], \047comments\047 => [ \047href\047 => route(\047api.posts.comments.index\047, $this->id), \047count\047 => $this->comments_count, ], ], // قسم HAL _embedded للموارد ذات الصلة \047_embedded\047 => [ \047author\047 => [ \047id\047 => $this->author->id, \047name\047 => $this->author->name, \047email\047 => $this->author->email, \047_links\047 => [ \047self\047 => [ \047href\047 => route(\047api.users.show\047, $this->author_id), ], ], ], ], ]; } }

توليد الروابط بناءً على الحالة

أحد أقوى جوانب HATEOAS هو إظهار الإجراءات المتاحة فقط بناءً على حالة المورد الحالية:

<?php class StatefulPostResource extends JsonResource { public function toArray($request) { $links = [ \047self\047 => route(\047api.posts.show\047, $this->id), ]; // إضافة روابط خاصة بالحالة if ($this->status === \047draft\047) { $links[\047publish\047] = [ \047href\047 => route(\047api.posts.publish\047, $this->id), \047method\047 => \047POST\047, ]; $links[\047edit\047] = [ \047href\047 => route(\047api.posts.update\047, $this->id), \047method\047 => \047PUT\047, ]; } if ($this->status === \047published\047) { $links[\047unpublish\047] = [ \047href\047 => route(\047api.posts.unpublish\047, $this->id), \047method\047 => \047POST\047, ]; } // السماح بالحذف فقط إذا لم يكن للمنشور تعليقات if ($this->comments_count === 0) { $links[\047delete\047] = [ \047href\047 => route(\047api.posts.destroy\047, $this->id), \047method\047 => \047DELETE\047, ]; } // السماح بالتحرير فقط إذا كان المستخدم هو المؤلف if ($this->author_id === $request->user()->id) { $links[\047edit\047] = [ \047href\047 => route(\047api.posts.update\047, $this->id), \047method\047 => \047PUT\047, ]; } return [ \047id\047 => $this->id, \047title\047 => $this->title, \047status\047 => $this->status, \047_links\047 => $links, ]; } }

إنشاء منشئ روابط قابل لإعادة الاستخدام

لتجنب التكرار، أنشئ خدمة مخصصة لبناء الروابط:

<?php namespace App\Services; class LinkBuilder { protected $links = []; public function add(string $rel, string $href, string $method = \047GET\047, array $extra = []) { $this->links[$rel] = array_merge([ \047href\047 => $href, \047method\047 => $method, ], $extra); return $this; } public function addIf(bool $condition, string $rel, string $href, string $method = \047GET\047) { if ($condition) { $this->add($rel, $href, $method); } return $this; } public function collection(string $rel, string $href) { return $this->add($rel, $href, \047GET\047, [\047type\047 => \047collection\047]); } public function toArray(): array { return $this->links; } } // الاستخدام في Resource public function toArray($request) { $linkBuilder = new LinkBuilder(); $linkBuilder->add(\047self\047, route(\047api.posts.show\047, $this->id)) ->addIf($this->canEdit(), \047edit\047, route(\047api.posts.update\047, $this->id), \047PUT\047) ->addIf($this->canDelete(), \047delete\047, route(\047api.posts.destroy\047, $this->id), \047DELETE\047) ->collection(\047comments\047, route(\047api.posts.comments.index\047, $this->id)); return [ \047id\047 => $this->id, \047title\047 => $this->title, \047_links\047 => $linkBuilder->toArray(), ]; }

الترقيم مع HATEOAS

HATEOAS ذو قيمة خاصة للمجموعات المرقمة:

<?php class PostCollection extends ResourceCollection { public function toArray($request) { return [ \047data\047 => $this->collection, \047_links\047 => [ \047self\047 => [ \047href\047 => $request->url(), ], \047first\047 => [ \047href\047 => $this->url(1), ], \047last\047 => [ \047href\047 => $this->url($this->lastPage()), ], \047prev\047 => $this->previousPageUrl() ? [ \047href\047 => $this->previousPageUrl(), ] : null, \047next\047 => $this->nextPageUrl() ? [ \047href\047 => $this->nextPageUrl(), ] : null, ], \047meta\047 => [ \047current_page\047 => $this->currentPage(), \047last_page\047 => $this->lastPage(), \047per_page\047 => $this->perPage(), \047total\047 => $this->total(), ], ]; } }

خطأ شائع: لا تقم بتضمين روابط لا يملك المستخدم الحالي إذنًا للوصول إليها. تحقق دائمًا من التفويض قبل إضافة روابط الإجراءات لتجنب إرباك العملاء أو تضليلهم.

متقدم: قوالب النماذج

للعمليات المعقدة، يمكنك تضمين قوالب نماذج تصف الحقول المطلوبة:

<?php public function toArray($request) { return [ \047id\047 => $this->id, \047title\047 => $this->title, \047_links\047 => [ \047update\047 => [ \047href\047 => route(\047api.posts.update\047, $this->id), \047method\047 => \047PUT\047, \047template\047 => [ \047title\047 => [ \047type\047 => \047string\047, \047required\047 => true, \047maxLength\047 => 255, ], \047content\047 => [ \047type\047 => \047text\047, \047required\047 => true, ], \047status\047 => [ \047type\047 => \047enum\047, \047required\047 => true, \047options\047 => [\047draft\047, \047published\047, \047archived\047], ], \047category_id\047 => [ \047type\047 => \047integer\047, \047required\047 => false, ], ], ], ], ]; }

تمرين: بناء واجهة برمجة تطبيقات HATEOAS

أنشئ تطبيقًا كاملاً لـ HATEOAS لواجهة برمجة تطبيقات منتجات:

  1. أنشئ ProductResource يتضمن روابط للعرض والتحرير والحذف والمراجعات والمنتجات ذات الصلة
  2. طبّق روابط قائمة على الحالة (مثل "إضافة إلى السلة" فقط إذا كان متوفرًا، "إعادة طلب" فقط إذا نفذ)
  3. أنشئ ProductCollection مع روابط الترقيم
  4. ابنِ خدمة LinkBuilder للحفاظ على مواردك جافة
  5. اختبر مع تطبيق عميل يتبع الروابط بدلاً من عناوين URL المشفرة

أفضل الممارسات لـ HATEOAS

  • استخدم العلاقات القياسية: فضّل علاقات الروابط المسجلة في IANA عندما يكون ذلك ممكنًا
  • كن متسقًا: استخدم نفس بنية الرابط عبر جميع الموارد
  • قم بتضمين الأساليب: حدد دائمًا طريقة HTTP لكل رابط
  • وثّق العلاقات المخصصة: وثّق بوضوح أي علاقات روابط مخصصة تنشئها
  • الإصدارات: يجب أن تكون علاقات الروابط مستقرة عبر إصدارات واجهة برمجة التطبيقات
  • الروابط الشرطية: قم فقط بتضمين الروابط التي يمكن للعميل استخدامها فعليًا
  • الأداء: ضع في اعتبارك استراتيجيات التخزين المؤقت لتوليد الروابط المكلفة
  • التوثيق: حتى مع HATEOAS، قدم توثيقًا واضحًا للعلاقات المتاحة

التأثير في العالم الحقيقي: تستخدم شركات مثل GitHub وPayPal مفهوم HATEOAS بشكل مكثف في واجهات برمجة التطبيقات الخاصة بها، مما يسمح لها بتطوير خدماتها دون كسر تطبيقات العملاء. هذه المرونة تصبح لا تقدر بثمن مع نمو وتغير واجهة برمجة التطبيقات الخاصة بك بمرور الوقت.