في تطوير الويب الحديث، أصبحت واجهات برمجة التطبيقات (APIs) التي تعتمد على نقل الحالة التمثيلية (REST) المعيار الفعلي لبناء خدمات الويب القابلة للتوسع والصيانة وسهلة الفهم. في قلب أي واجهة برمجة تطبيقات RESTful يكمن مفهوم أساسي: الـ مورد. فهم ما هي الموارد، وكيف يتم تحديدها، وكيف نتفاعل معها أمر بالغ الأهمية لتصميم واجهات برمجة تطبيقات REST واستهلاكها بفعالية. سيتعمق هذا المقال في طبيعة الموارد في واجهات برمجة تطبيقات REST، مستكشفًا خصائصها، وعلاقتها بالمجموعات، والعمليات الشائعة التي يتم إجراؤها عليها.
الفكرة الأساسية وراء REST هي أن واجهة برمجة التطبيقات تعرض مجموعة من الموارد، ويتفاعل العملاء مع هذه الموارد عن طريق إرسال طلبات إلى معرفاتها الفريدة. ولكن ما الذي يشكل "موردًا" بالضبط؟ في سياق واجهة برمجة تطبيقات REST، يمكن أن يكون المورد أي شيء تقريبًا يمكنك تسميته. يمكن أن يكون كيانًا ملموسًا مثل عميل أو منتج أو طلب. يمكن أن يكون أيضًا مفهومًا مجردًا مثل خدمة أو معاملة أو عملية حسابية. المفتاح هو أنه عنصر اهتمام يمكن تحديده والتلاعب به.
فكر في الإنترنت نفسه كمجموعة واسعة من الموارد. كل صفحة ويب أو صورة أو فيديو أو مستند تصل إليه عبر الإنترنت هو مورد، لكل منها عنوان فريد خاص به (URL). تتبنى واجهات برمجة تطبيقات REST هذه الفلسفة نفسها. سواء كان ملف تعريف مستخدم على منصة تواصل اجتماعي، أو كتاب معين في مكتبة عبر الإنترنت، أو توقعات طقس لمدينة معينة، فإن كل منها هو مورد تتيحه واجهة برمجة التطبيقات.
هل تريد منصة متكاملة وشاملة لفريق المطورين لديك للعمل معًا بأقصى إنتاجية؟
Apidog يلبي جميع متطلباتك، ويحل محل Postman بسعر معقول أكثر بكثير!
تحديد الموارد: دور معرفات الموارد الموحدة (URIs)
والأهم من ذلك، يجب أن يحتوي كل مورد في واجهة برمجة تطبيقات REST على معرف فريد واحد على الأقل. هذا المعرف هو عادةً معرف الموارد الموحد (URI). الشكل الأكثر شيوعًا لمعرف URI المستخدم في واجهات برمجة تطبيقات الويب هو محدد الموارد الموحد (URL)، والذي لا يحدد المورد فحسب، بل يوفر أيضًا وسيلة لتحديد مكانه.
على سبيل المثال، في واجهة برمجة تطبيقات لإدارة مدونة، يمكن تحديد منشور مدونة معين بواسطة URI مثل /posts/123
. هنا، /posts
يمثل على الأرجح مجموعة من المنشورات، و 123
هو المعرف الفريد لمنشور معين داخل تلك المجموعة. وبالمثل، يمكن تحديد مورد مستخدم بواسطة /users/john.doe
.
تصميم هذه المعرفات (URIs) هو جانب حاسم في تصميم واجهات برمجة التطبيقات. المعرفات المصممة جيدًا تكون بديهية، قابلة للتنبؤ، وسهلة للمطورين للفهم والاستخدام. يجب أن تعمل كعلامة إرشادية واضحة، تشير إلى طبيعة المورد الذي يتم الوصول إليه. تملي الممارسة الجيدة استخدام الأسماء لتمثيل الموارد (مثل /products
، /orders
) بدلاً من الأفعال (مثل /getProducts
، /createOrder
). ثم يتم استخدام أساليب HTTP (GET، POST، PUT، DELETE) لتحديد الإجراء الذي سيتم تنفيذه على المورد المحدد بواسطة URI.
المورد مقابل التمثيل: تمييز أساسي
من المهم فهم الفرق بين المورد وتمثيله. المورد هو الكيان المفاهيمي نفسه - "الشيء" الفعلي (العميل، المنتج، الفكرة). التمثيل، من ناحية أخرى، هو لقطة لحالة هذا المورد في نقطة زمنية معينة، عادةً ما يتم تنسيقه في نوع وسائط معين مثل JSON (JavaScript Object Notation) أو XML (eXetensible Markup Language).
عندما يطلب العميل موردًا من واجهة برمجة تطبيقات، فإنه لا يتلقى المورد نفسه (وهو مفهوم مجرد يقيم على الخادم). بدلاً من ذلك، يتلقى تمثيلًا لهذا المورد. على سبيل المثال، إذا طلبت /users/jane.doe
، فقد ترجع واجهة برمجة التطبيقات تمثيلًا بصيغة JSON مثل:JSON
{
"id": "jane.doe",
"firstName": "Jane",
"lastName": "Doe",
"email": "jane.doe@example.com",
"dateJoined": "2023-01-15T10:00:00Z"
}
كائن JSON هذا ليس Jane Doe نفسها؛ إنه تمثيل لبياناتها كما هي مخزنة بواسطة النظام. يمكن أن يكون لنفس المورد عدة تمثيلات محتملة. على سبيل المثال، قد تكون واجهة برمجة التطبيقات قادرة أيضًا على توفير تمثيل XML لنفس المستخدم إذا طلب العميل ذلك من خلال تفاوض المحتوى (باستخدام رؤوس HTTP مثل Accept
).
يمكن أن تتغير حالة المورد بمرور الوقت. إذا قامت Jane Doe بتحديث عنوان بريدها الإلكتروني، تتغير مورد المستخدم الأساسي. الطلبات اللاحقة لـ /users/jane.doe
ستعيد بعد ذلك تمثيلًا جديدًا يعكس هذه الحالة المحدثة. هذا هو المكان الذي يأتي فيه جزء "نقل الحالة" (State Transfer) من REST: يتفاعل العملاء مع الموارد عن طريق استرداد حالاتها والتلاعب بها من خلال هذه التمثيلات.
المجموعات: موارد تحتوي على موارد أخرى
غالبًا ما يتم تجميع الموارد معًا في مجموعات. المجموعة هي بحد ذاتها مورد. على سبيل المثال، /posts
في واجهة برمجة تطبيقات المدونة الخاصة بنا هو مورد مجموعة يحتوي على موارد منشورات فردية. وبالمثل، سيكون /users
مجموعة من موارد المستخدمين.
عندما يرسل العميل طلب GET إلى URI مجموعة مثل /products
، فإن واجهة برمجة التطبيقات عادةً ما ترجع تمثيلًا يسرد الموارد الأعضاء داخل تلك المجموعة، غالبًا مع بعض المعلومات الملخصة لكل منها. قد تبدو هذه القائمة شيئًا مثل:JSON
[
{
"id": "prod_abc",
"name": "Laptop Pro 15",
"price": 1299.99,
"link": "/products/prod_abc"
},
{
"id": "prod_xyz",
"name": "Wireless Mouse Ergonomic",
"price": 39.99,
"link": "/products/prod_xyz"
},
// ... more products
]
لاحظ كيف يتضمن كل عنصر في المجموعة غالبًا رابطًا (أو URI الخاص به) إلى المورد الفردي، مما يسمح للعميل بالانتقال إلى التفاصيل الكاملة لمنتج معين واستردادها إذا لزم الأمر.
تصميم معرفات الموارد (URIs) للمجموعات وأعضائها يتبع نمطًا منطقيًا. عادةً:
/resources
يشير إلى المجموعة (مثل/orders
)./resources/{id}
يشير إلى عضو معين داخل تلك المجموعة (مثل/orders/567
).
هذا الهيكل الهرمي يجعل واجهة برمجة التطبيقات بديهية ويتوافق مع العلاقة المفاهيمية بين المجموعة وعناصرها.
العمليات على الموارد والمجموعات
يتم التفاعل مع الموارد والمجموعات في واجهة برمجة تطبيقات REST من خلال أساليب HTTP القياسية. تحدد هذه الأساليب الإجراءات التي سيتم تنفيذها. الأساليب الأكثر شيوعًا هي:
GET: يُستخدم لاسترداد تمثيل لمورد أو مجموعة.
GET /posts
سيسترد قائمة بجميع المنشورات (المجموعة).GET /posts/123
سيسترد المنشور المحدد بالمعرف 123. يجب أن تكون طلبات GET آمنة، مما يعني أنه لا ينبغي أن يكون لها أي آثار جانبية على الخادم؛ إنها فقط لاسترداد البيانات. يجب أن تكون أيضًا مكتملة الأثر (idempotent)، مما يعني أن طلبات GET المتعددة المتطابقة يجب أن يكون لها نفس تأثير طلب واحد (أي، إرجاع نفس التمثيل، بافتراض أن المورد لم يتغير في هذه الأثناء).
POST: يُستخدم بشكل أساسي لإنشاء مورد جديد داخل مجموعة. يحتوي نص الطلب عادةً على تمثيل المورد الجديد المراد إنشاؤه.
POST /posts
مع نص طلب يحتوي على تفاصيل منشور مدونة جديد (مثل العنوان، المحتوى، المؤلف) سيوجه الخادم لإنشاء هذا المنشور الجديد. يستجيب الخادم عادةً بحالة201 Created
وغالبًا ما يتضمن URI للمورد الذي تم إنشاؤه حديثًا في رأسLocation
للاستجابة. طلبات POST ليست آمنة بشكل عام (لأنها تنشئ موردًا جديدًا) وليست مكتملة الأثر (طلبات POST المتعددة المتطابقة ستنشئ عادةً موارد جديدة متعددة). يمكن أيضًا استخدام POST لعمليات أخرى غير مكتملة الأثر لا تتناسب تمامًا مع أساليب HTTP الأخرى، مثل تشغيل عملية أو إرسال بيانات للمعالجة حيث لا تكون النتيجة مجرد تحديث لمورد موجود قابل للتحديد.
PUT: يُستخدم لتحديث مورد موجود بشكل كامل. يجب أن يحتوي نص الطلب على التمثيل الكامل الجديد للمورد. إذا كان المورد المحدد بواسطة URI موجودًا، يتم استبداله بالتمثيل الجديد. إذا لم يكن موجودًا، قد تختار بعض واجهات برمجة التطبيقات إنشائه (على الرغم من أن هذا السلوك يمكن أن يختلف).
PUT /posts/123
مع نص طلب يحتوي على العنوان والمحتوى المحدثين للمنشور 123 سيحل محل المنشور 123 الحالي بالبيانات الجديدة. طلبات PUT ليست آمنة (لأنها تعدل موردًا) ولكنها مكتملة الأثر. إرسال نفس طلب PUT عدة مرات يجب أن يؤدي إلى نفس الحالة للمورد. على سبيل المثال، تحديث عنوان منشور إلى "عنوان جديد" عدة مرات لا يزال يؤدي إلى أن يكون العنوان "عنوان جديد".
DELETE: يُستخدم لإزالة مورد.
DELETE /posts/123
سيحذف منشور المدونة بالمعرف 123. طلبات DELETE ليست آمنة (فهي تزيل البيانات) ولكنها مكتملة الأثر. حذف مورد عدة مرات يجب أن يكون له نفس نتيجة حذفه مرة واحدة (المورد قد زال). طلبات DELETE اللاحقة لنفس URI قد ترجع404 Not Found
أو204 No Content
.
PATCH: يُستخدم لتحديث مورد موجود جزئيًا. على عكس PUT، الذي يتطلب من العميل إرسال التمثيل الكامل للمورد، يسمح PATCH بإرسال التغييرات فقط. على سبيل المثال، لتحديث عنوان البريد الإلكتروني لمستخدم فقط، يحتاج طلب PATCH فقط إلى تضمين البريد الإلكتروني الجديد.
PATCH /users/jane.doe
مع نص طلب مثل{"email": "new.email@example.com"}
سيحدث فقط عنوان البريد الإلكتروني لـ Jane، تاركًا الحقول الأخرى مثل اسمها دون تغيير. طلبات PATCH ليست آمنة. يمكن مناقشة اكتمال أثرها ويعتمد على طبيعة عملية PATCH. على سبيل المثال، عملية PATCH تقول "إلحاق '!' بالوصف" ليست مكتملة الأثر، في حين أن "تعيين الوصف إلى 'قيمة جديدة'" هي كذلك.
الموارد الفردية (Singleton Resources)
بينما المجموعات وأعضاؤها شائعة، أحيانًا يكون المورد كيانًا قائمًا بذاته، وغالبًا ما يشار إليه على أنه مورد "فردي" (singleton). مثال جيد قد يكون تكوين تطبيق معين أو الحالة الحالية لنظام.
على سبيل المثال، /application/configuration
يمكن أن يكون URI لمورد فردي يمثل إعدادات تكوين التطبيق. طلب GET
إلى هذا URI سيسترد التكوين الحالي، ويمكن استخدام طلب PUT
لتحديثه. لا توجد "مجموعة" من التكوينات في هذا السياق؛ هناك فقط التكوين. وبالمثل، يمكن أن يمثل /system/status
حالة التشغيل الحالية للنظام.
أفضل الممارسات لتصميم واجهات برمجة التطبيقات القائمة على الموارد
تصميم واجهة برمجة تطبيقات تتمحور حول الموارد يتضمن أكثر من مجرد تحديد الكيانات وتعيينها لمعرفات الموارد (URIs). تساهم العديد من أفضل الممارسات في واجهة برمجة تطبيقات قوية وسهلة الاستخدام:
- استخدام الأسماء في معرفات الموارد (URIs): كما ذكرنا سابقًا، يجب أن تكون معرفات الموارد (URIs) أسماء (مثل
/products
،/users/{userId}/orders
). يجب حجز الأفعال لأساليب HTTP. - تسمية معرفات الموارد (URIs) بشكل متناسق: استخدم اتفاقية تسمية متناسقة لمعرفات الموارد (URIs) الخاصة بك. تُفضل الأسماء بصيغة الجمع بشكل عام للمجموعات (مثل
/customers
بدلاً من/customer
). استخدم الواصلات (-
) لتحسين قابلية قراءة أجزاء المسار الطويلة (مثل/product-categories
) بدلاً من الشرطات السفلية (_
) أو camelCase. - الحفاظ على معرفات الموارد (URIs) بسيطة وهرمية: صمم معرفات الموارد (URIs) التي تعكس العلاقات بين الموارد. على سبيل المثال،
/users/{userId}/accounts/{accountId}
يوضح بوضوح أن الحساب ينتمي إلى مستخدم. ومع ذلك، تجنب التعشيش العميق المفرط، الذي يمكن أن يجعل معرفات الموارد (URIs) غير عملية. - عدم الاحتفاظ بالحالة (Statelessness): يجب أن يحتوي كل طلب من العميل إلى الخادم على جميع المعلومات اللازمة لفهم1 ومعالجة الطلب. لا ينبغي للخادم2 تخزين أي سياق للعميل بين الطلبات. هذا مبدأ أساسي في REST ويساهم في قابلية التوسع.
- استخدام أساليب HTTP بشكل صحيح: استخدم GET، POST، PUT، DELETE، و PATCH وفقًا لدلالاتها المحددة. لا تستخدم GET لتعديل البيانات أو POST لاسترداد البيانات عندما يكون GET مناسبًا.
- استخدام رموز حالة HTTP بشكل مناسب: قم بإرجاع رموز حالة HTTP القياسية للإشارة إلى نتيجة الطلب (مثل
200 OK
،201 Created
،204 No Content
،400 Bad Request
،401 Unauthorized
،403 Forbidden
،3404 Not Found
،500 Internal Server Error
). هذا يوفر تغذية راجعة واضحة للعميل. - دعم تفاوض المحتوى: اسمح للعملاء بتحديد تنسيق التمثيل المطلوب (مثل JSON، XML) باستخدام رأس
Accept
والإشارة إلى تنسيق نص الطلب باستخدام رأسContent-Type
. - إصدار الواجهة (Versioning): خطط لتطور واجهة برمجة التطبيقات عن طريق تطبيق استراتيجية إصدار (مثل
/v1/products
). هذا يسمح لك بتقديم تغييرات جذرية دون التأثير على العملاء الحاليين. - توفير تمثيلات خطأ ذات معنى: عند حدوث خطأ، قم بإرجاع رسالة خطأ مفيدة في نص الاستجابة (عادةً JSON أو XML) تشرح ما حدث بشكل خاطئ.
- الوسائط الفائقة كمحرك لحالة التطبيق (HATEOAS): على الرغم من أنها لا تُطبق دائمًا بشكل كامل، إلا أن HATEOAS مبدأ أساسي في REST. يعني أن تمثيلات الموارد يجب أن تتضمن روابط (عناصر تحكم وسائط فائقة) تسمح للعملاء باكتشاف الإجراءات والموارد ذات الصلة. على سبيل المثال، تمثيل لطلب قد يتضمن روابط لإلغاء الطلب، عرض حالة شحنه، أو رؤية المنتجات التي يحتوي عليها. هذا يجعل واجهة برمجة التطبيقات أكثر قابلية للاكتشاف الذاتي.
دقة تفاصيل الموارد (Granularity)
أحد تحديات التصميم الشائعة هو تحديد دقة تفاصيل الموارد المناسبة. هل يجب أن يكون العنوان موردًا منفصلاً، أم جزءًا من مورد مستخدم؟ هل يجب أن تكون عناصر الطلب موارد مميزة، أم مضمنة داخل مورد طلب؟
لا يوجد إجابة واحدة صحيحة؛ يعتمد ذلك على حالات الاستخدام المحددة وكيف سيتفاعل العملاء عادةً مع البيانات.
- موارد منفصلة: إذا كان الكيان (مثل العنوان) يمكن إنشاؤه أو استرداده أو تحديثه أو حذفه بشكل مستقل، أو إذا كان مشتركًا بين موارد أخرى متعددة، فغالبًا ما يكون من المنطقي نمذجته كمورد منفصل بـ URI خاص به (مثل
/addresses/{addressId}
). يمكنك بعد ذلك ربطه من موارد أخرى (على سبيل المثال، قد يحتوي مورد مستخدم على حقلaddressId
أو رابط إلى/addresses/{addressId}
). - موارد مضمنة / موارد فرعية: إذا كان الكيان مرتبطًا ارتباطًا وثيقًا بمورد رئيسي وليس له دورة حياة مستقلة، فقد يكون من الأفضل نمذجته كجزء من تمثيل المورد الرئيسي أو كمورد فرعي يمكن الوصول إليه عبر مسار مثل
/users/{userId}/address
. هذا يمكن أن يبسط تفاعلات العميل إذا كان يتم الوصول إلى الكيان الفرعي دائمًا في سياق مورده الرئيسي.
غالبًا ما يتضمن الاختيار مقايضات:
- كثرة الطلبات (Chattiness): قد تؤدي الموارد الدقيقة التفاصيل إلى المزيد من طلبات HTTP (زيادة كثرة الطلبات) إذا احتاج العميل إلى تجميع صورة كاملة من مصادر متعددة.
- تكرار البيانات / التعقيد: قد يؤدي تضمين الموارد إلى حمولات أكبر وتكرار محتمل للبيانات أو تعقيد إذا كانت المعلومات المضمنة متاحة أيضًا كمورد مستقل.
- قابلية التخزين المؤقت (Cacheability): الموارد المنفصلة غالبًا ما تكون أسهل للتخزين المؤقت بشكل مستقل.
- ذرية العمليات (Atomicity of Operations): التحديثات على مورد واحد، خشن التفاصيل، تكون ذرية بطبيعتها. إدارة الذرية عبر تحديثات موارد متعددة دقيقة التفاصيل يمكن أن تكون أكثر تعقيدًا.
إن التفكير الدقيق في هذه العوامل، جنبًا إلى جنب مع فهم عميق لكيفية استخدام واجهة برمجة التطبيقات، أمر بالغ الأهمية لاتخاذ القرارات الصحيحة بشأن دقة تفاصيل الموارد.
هل تريد منصة متكاملة وشاملة لفريق المطورين لديك للعمل معًا بأقصى إنتاجية؟
Apidog يلبي جميع متطلباتك، ويحل محل Postman بسعر معقول أكثر بكثير!
خاتمة
الموارد هي اللبنات الأساسية لأي واجهة برمجة تطبيقات RESTful. إنها تمثل "الأشياء" التي تعرضها واجهة برمجة التطبيقات وتسمح للعملاء بالتفاعل معها. من خلال تعيين معرفات موارد فريدة (URIs) للموارد، والتمييز بين المورد وتمثيله، وتنظيم الموارد في مجموعات منطقية، يمكن للمطورين إنشاء واجهات برمجة تطبيقات بديهية، قابلة للتوسع، وتلتزم بمبادئ REST.
فهم كيفية تعريف الموارد وتحديدها والتلاعب بها باستخدام أساليب HTTP القياسية أمر ضروري لكل من مصممي ومستهلكي واجهات برمجة التطبيقات. مقترنًا بأفضل الممارسات في تصميم معرفات الموارد (URIs)، والاستخدام المناسب لرموز حالة HTTP، والنهج المدروس لدقة تفاصيل الموارد، يؤدي نموذج المورد المحدد جيدًا إلى واجهات برمجة تطبيقات ليست وظيفية فحسب، بل ممتعة أيضًا للعمل معها. مع استمرار تطور المشهد الرقمي، ستظل مبادئ بنية الموارد محورًا أساسيًا للتواصل الفعال لخدمات الويب.