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

تنسيقات الطلب والاستجابة

18 دقيقة الدرس 4 من 35

فهم تنسيقات البيانات في واجهات برمجة REST

تحتاج واجهات برمجة REST إلى لغة مشتركة لتبادل البيانات بين العملاء والخوادم. بينما لا تفرض REST نفسها تنسيقاً محدداً، أصبح JSON المعيار الفعلي بسبب بساطته وقابليته للقراءة ودعمه العالمي عبر لغات البرمجة.

ملاحظة: بينما يغطي هذا الدرس تنسيقات متعددة، 95٪ من واجهات برمجة REST الحديثة تستخدم JSON حصرياً. ركز على إتقان هيكل JSON وأفضل الممارسات.

JSON: ملك تنسيقات بيانات واجهة البرمجة

JSON (JavaScript Object Notation) خفيف الوزن، قابل للقراءة البشرية، وسهل التحليل. إنه المعيار لواجهات برمجة REST الحديثة.

أساسيات JSON:

{
  "id": 123,
  "name": "أحمد محمد",
  "email": "ahmed@example.com",
  "age": 30,
  "isActive": true,
  "roles": ["user", "editor"],
  "address": {
    "street": "شارع الرئيسي 123",
    "city": "القاهرة",
    "zipCode": "11511"
  },
  "metadata": null
}

أنواع بيانات JSON:

  • نص (String): "نص بين علامتي اقتباس مزدوجة"
  • رقم (Number): 123, 45.67 (بدون علامات اقتباس)
  • منطقي (Boolean): true, false (أحرف صغيرة، بدون علامات اقتباس)
  • مصفوفة (Array): ["item1", "item2"]
  • كائن (Object): {"key": "value"}
  • null: null (يمثل غياب القيمة)
تحذير: لا يدعم JSON التعليقات أو الفواصل الزائدة أو علامات الاقتباس الفردية. تحقق دائماً من صحة JSON الخاص بك باستخدام linter قبل النشر.

هيكلة استجابات JSON

الهيكل المتسق للاستجابة يجعل واجهتك البرمجية قابلة للتنبؤ وسهلة الاستخدام.

استجابة مورد واحد:

GET /api/users/123

HTTP/1.1 200 OK
Content-Type: application/json

{
  "data": {
    "id": 123,
    "name": "أحمد محمد",
    "email": "ahmed@example.com",
    "created_at": "2026-01-15T10:30:00Z",
    "updated_at": "2026-02-14T14:20:00Z"
  }
}

استجابة مجموعة:

GET /api/users?page=1&limit=2

HTTP/1.1 200 OK
Content-Type: application/json

{
  "data": [
    {
      "id": 123,
      "name": "أحمد محمد",
      "email": "ahmed@example.com"
    },
    {
      "id": 124,
      "name": "فاطمة علي",
      "email": "fatima@example.com"
    }
  ],
  "meta": {
    "current_page": 1,
    "per_page": 2,
    "total": 150,
    "total_pages": 75
  },
  "links": {
    "first": "/api/users?page=1",
    "prev": null,
    "next": "/api/users?page=2",
    "last": "/api/users?page=75"
  }
}

استجابة خطأ:

POST /api/users

HTTP/1.1 422 Unprocessable Entity
Content-Type: application/json

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "فشلت بيانات الطلب في التحقق",
    "details": [
      {
        "field": "email",
        "message": "البريد الإلكتروني مطلوب",
        "code": "REQUIRED_FIELD"
      },
      {
        "field": "age",
        "message": "يجب أن يكون العمر 18 على الأقل",
        "code": "MIN_VALUE"
      }
    ]
  }
}
نصيحة: غلف بيانات استجابتك في كائن "data" حتى للموارد الفردية. هذا يسمح لك بإضافة بيانات وصفية لاحقاً دون كسر العملاء.

اصطلاحات تسمية JSON

اختر نمط تسمية والتزم به في جميع أنحاء واجهتك البرمجية.

camelCase (موصى به لواجهات برمجة JavaScript الثقيلة):

{
  "userId": 123,
  "firstName": "أحمد",
  "lastName": "محمد",
  "emailAddress": "ahmed@example.com",
  "isEmailVerified": true,
  "createdAt": "2026-01-15T10:30:00Z"
}

snake_case (شائع في واجهات برمجة Python/Ruby):

{
  "user_id": 123,
  "first_name": "أحمد",
  "last_name": "محمد",
  "email_address": "ahmed@example.com",
  "is_email_verified": true,
  "created_at": "2026-01-15T10:30:00Z"
}

PascalCase (نادر في واجهات برمجة REST):

{
  "UserId": 123,
  "FirstName": "أحمد",
  "LastName": "محمد"
}
مهم: مطورو JavaScript و TypeScript يفضلون camelCase. مطورو Python و Ruby يفضلون snake_case. اختر ما يناسب قاعدة عملائك الأساسية، ولكن كن متسقاً.

تنسيقات التاريخ والوقت

استخدم دائماً تنسيق ISO 8601 للتواريخ والأوقات. إنه واضح ومدعوم عالمياً.

أمثلة ISO 8601:

// التاريخ فقط
"2026-02-14"

// التاريخ والوقت (UTC - موصى به)
"2026-02-14T10:30:00Z"

// التاريخ والوقت مع إزاحة المنطقة الزمنية
"2026-02-14T10:30:00-05:00"

// التاريخ والوقت مع ملي ثانية
"2026-02-14T10:30:00.123Z"
نصيحة: قم دائماً بتخزين ونقل التواريخ في UTC (يُشار إليها بـ Z). دع العملاء يحولون إلى منطقتهم الزمنية المحلية للعرض.

Null مقابل الحقول المفقودة

قرر كيفية التعامل مع البيانات المفقودة وكن متسقاً.

الخيار 1: تضمين قيم Null

{
  "id": 123,
  "name": "أحمد محمد",
  "middleName": null,
  "phone": null,
  "bio": null
}

المزايا:
✅ صريح بشأن البيانات المفقودة
✅ العميل يعرف أن الحقل موجود
✅ التحقق من المخطط أسهل

العيوب:
❌ حجم استجابة أكبر
❌ أكثر تفصيلاً

الخيار 2: حذف قيم Null

{
  "id": 123,
  "name": "أحمد محمد"
}

المزايا:
✅ حجم استجابة أصغر
✅ أنظف، أقل فوضى

العيوب:
❌ لا يمكن التمييز بين null وundefined
❌ يجب على العميل التعامل مع المفاتيح المفقودة

رؤوس HTTP: طبقة البيانات الوصفية

توفر الرؤوس معلومات مهمة حول الطلبات والاستجابات دون تشويش الجسم.

رأس Content-Type

يخبر الخادم/العميل بتنسيق البيانات.

// الطلب
POST /api/users
Content-Type: application/json

{"name": "أحمد محمد"}

// الاستجابة
HTTP/1.1 200 OK
Content-Type: application/json

{"id": 123, "name": "أحمد محمد"}

رأس Accept

يخبر العميل الخادم بالتنسيقات التي يمكنه التعامل معها.

// العميل يفضل JSON
GET /api/users
Accept: application/json

// العميل يمكنه التعامل مع تنسيقات متعددة (ترتيب الأفضلية)
GET /api/users
Accept: application/json, application/xml;q=0.9, */*;q=0.8
قيم الجودة (q): معامل q (0-1) يشير إلى التفضيل. القيم الأعلى = تفضيل أعلى. الافتراضي هو q=1.0.

أنواع المحتوى الشائعة:

Content-Type الوصف الاستخدام
application/json بيانات JSON 99٪ من واجهات برمجة REST
application/xml بيانات XML الأنظمة القديمة
application/x-www-form-urlencoded بيانات النموذج نماذج HTML
multipart/form-data تحميلات الملفات الصور، المستندات
text/plain نص عادي استجابات بسيطة
text/html مستند HTML توثيق واجهة البرمجة

التفاوض على المحتوى

يسمح التفاوض على المحتوى لنفس نقطة النهاية بإرجاع تنسيقات مختلفة بناءً على تفضيل العميل.

التفاوض الذي يقوده الخادم:

// العميل يطلب JSON
GET /api/users/123
Accept: application/json

الاستجابة:
HTTP/1.1 200 OK
Content-Type: application/json

{"id": 123, "name": "أحمد محمد"}

// العميل يطلب XML
GET /api/users/123
Accept: application/xml

الاستجابة:
HTTP/1.1 200 OK
Content-Type: application/xml

<user>
  <id>123</id>
  <name>أحمد محمد</name>
</user>

التعامل مع التنسيقات غير المدعومة:

GET /api/users/123
Accept: application/pdf

الاستجابة:
HTTP/1.1 406 Not Acceptable
Content-Type: application/json

{
  "error": {
    "code": "UNSUPPORTED_MEDIA_TYPE",
    "message": "لا يمكن للخادم إنتاج استجابة بالتنسيق المطلوب",
    "supported_formats": ["application/json", "application/xml"]
  }
}

XML: التنسيق البديل

بينما يهيمن JSON، لا تزال بعض أنظمة المؤسسات تستخدم XML.

مثال XML:

<?xml version="1.0" encoding="UTF-8"?>
<user>
  <id>123</id>
  <name>أحمد محمد</name>
  <email>ahmed@example.com</email>
  <roles>
    <role>user</role>
    <role>editor</role>
  </roles>
  <address>
    <street>شارع الرئيسي 123</street>
    <city>القاهرة</city>
  </address>
</user>

مقارنة JSON مقابل XML:

الجانب JSON XML
قابلية القراءة قابل للقراءة جداً مطول
الحجم أصغر (30-50٪ أقل) أكبر
سرعة التحليل أسرع أبطأ
أنواع البيانات أنواع أصلية كل شيء نص
المصفوفات دعم أصلي عناصر متكررة
التحقق JSON Schema XSD، DTD
التعليقات غير مدعوم مدعوم
تحذير: ما لم يكن لديك متطلب محدد (الأنظمة القديمة، تكامل المؤسسات)، التزم بـ JSON. يضيف XML تعقيداً غير ضروري لمعظم واجهات برمجة REST.

تحميلات الملفات: multipart/form-data

عند تحميل الملفات، استخدم multipart/form-data بدلاً من JSON.

POST /api/users/123/avatar
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary

------WebKitFormBoundary
Content-Disposition: form-data; name="file"; filename="avatar.jpg"
Content-Type: image/jpeg

[بيانات ملف ثنائية]
------WebKitFormBoundary
Content-Disposition: form-data; name="description"

صورة ملف شخصي جديدة
------WebKitFormBoundary--

الاستجابة:
HTTP/1.1 201 Created
Content-Type: application/json

{
  "data": {
    "id": 456,
    "filename": "avatar.jpg",
    "size": 51234,
    "url": "https://cdn.example.com/avatars/456.jpg",
    "uploaded_at": "2026-02-14T10:30:00Z"
  }
}

الضغط: تقليل النطاق الترددي

تمكين الضغط لتقليل أحجام الاستجابة بنسبة 60-80٪.

// العميل يقبل الاستجابة المضغوطة
GET /api/users
Accept-Encoding: gzip, deflate, br

// الخادم يرسل استجابة مضغوطة
HTTP/1.1 200 OK
Content-Type: application/json
Content-Encoding: gzip
Content-Length: 1234

[بيانات مضغوطة]
نصيحة: معظم خوادم الويب (Nginx، Apache) والأطر (Express، Laravel) تدعم الضغط التلقائي. قم بتمكينه في الإنتاج لتقليل تكاليف النطاق الترددي بشكل كبير.

ترميز الأحرف: UTF-8 دائماً

استخدم دائماً ترميز UTF-8 لدعم الأحرف الدولية.

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{
  "name": "José García",
  "city": "São Paulo",
  "greeting": "你好",
  "message": "مرحبا"
}

قائمة التحقق من أفضل الممارسات

  • ✅ استخدم JSON كتنسيق أساسي
  • ✅ قم دائماً بتعيين رؤوس Content-Type بشكل صحيح
  • ✅ استخدم ISO 8601 لجميع التواريخ والأوقات
  • ✅ كن متسقاً مع التسمية (camelCase أو snake_case)
  • ✅ غلف الاستجابات في كائن "data" للمرونة
  • ✅ قم بتضمين البيانات الوصفية للترقيم للمجموعات
  • ✅ وفر رسائل خطأ مفصلة مع رموز
  • ✅ دعم الضغط (gzip، brotli)
  • ✅ استخدم ترميز UTF-8 في كل مكان
  • ✅ أرجع 406 لأنواع Accept غير المدعومة
تمرين: هيكلة استجابات واجهة البرمجة

أنشئ هياكل استجابة JSON مناسبة لهذه السيناريوهات:

  1. إنشاء مستخدم ناجح: معرف المستخدم 789، الاسم "سارة أحمد"، البريد الإلكتروني "sara@example.com"، تم إنشاؤه اليوم في تمام الساعة 10:30 صباحاً UTC
  2. خطأ التحقق: البريد الإلكتروني غير صالح، كلمة المرور قصيرة جداً (8 أحرف كحد أدنى)
  3. مجموعة مع الترقيم: 3 منتجات من إجمالي 150، الصفحة 1 من 50، 3 عناصر لكل صفحة

حل عينة للسؤال #1:

HTTP/1.1 201 Created
Content-Type: application/json
Location: /api/users/789

{
  "data": {
    "id": 789,
    "name": "سارة أحمد",
    "email": "sara@example.com",
    "created_at": "2026-02-14T10:30:00Z"
  }
}

النقاط الرئيسية

  • JSON هو التنسيق القياسي لواجهات برمجة REST الحديثة
  • استخدم دائماً رؤوس Content-Type و Accept بشكل صحيح
  • تنسيق ISO 8601 للتواريخ (UTC مع لاحقة Z)
  • كن متسقاً مع اصطلاحات التسمية في جميع أنحاء واجهتك البرمجية
  • غلف الاستجابات في كائن "data" للمرونة المستقبلية
  • قم بتضمين بيانات وصفية مفيدة (الترقيم، الطوابع الزمنية)
  • دعم الضغط لتقليل النطاق الترددي
  • استخدم ترميز UTF-8 للدعم الدولي
معاينة الدرس التالي: لقد تعلمت النظرية - حان الوقت للبناء! في الدرس التالي، سننشئ أول واجهة برمجة REST من الصفر، نطبق كل ما تعلمته حتى الآن.