البرمجة

استخدام Fetch مع طلبات CORS

جدول المحتوى

استخدام Fetch مع الطلبات ذات الأصل المختلط Cross-Origin في جافاسكربت

في عالم تطوير الويب الحديث، أصبحت التطبيقات تعتمد بشكل متزايد على تبادل البيانات بين الخوادم المختلفة، وهذا يتطلب التعامل مع ما يُعرف بالطلبات ذات الأصل المختلط أو Cross-Origin Requests. هذه الطلبات تحدث عندما تحاول صفحة ويب تحميل أو إرسال بيانات إلى دومين أو بروتوكول أو منفذ مختلف عن الذي تم تحميل الصفحة منه. ونظرًا لأن هذا النوع من التفاعل يمكن أن يشكل تهديدًا أمنيًا، تم وضع سياسات وضوابط صارمة لتنظيمه، أبرزها سياسة Same-Origin Policy. في هذا المقال، سيتم التعمق في مفهوم الطلبات ذات الأصل المختلط، التحديات التي تواجهها عند استخدام Fetch API في جافاسكربت، وكيفية التعامل معها بطريقة صحيحة وآمنة.


1. تعريف الطلبات ذات الأصل المختلط Cross-Origin Requests

عند زيارة صفحة ويب، يتم تحميل هذه الصفحة من خادم (Origin) معين، ويُعرف الأصل بأنه تركيبة من البروتوكول (http أو https)، اسم الدومين، ورقم المنفذ. على سبيل المثال:

عندما تحاول صفحة الويب إجراء طلب إلى أصل مختلف عن الأصل الذي تم تحميل الصفحة منه، فإن هذا يُعرف بطلب ذات أصل مختلط أو Cross-Origin Request.


2. سياسة Same-Origin Policy وأهميتها

سياسة Same-Origin Policy هي آلية أمنية مطبقة في المتصفحات بهدف منع المواقع الضارة من الوصول إلى بيانات المواقع الأخرى بطريقة غير مصرح بها. هذه السياسة تمنع تنفيذ معظم أنواع الطلبات أو التفاعل مع الموارد من أصل مختلف بشكل مباشر.

ومع ذلك، في تطبيقات الويب الحديثة، يكون من الضروري أحيانًا إجراء طلبات Cross-Origin للحصول على موارد من واجهات برمجة التطبيقات (APIs) المستضافة على نطاقات مختلفة. لذلك، تم تطوير آلية تُسمى Cross-Origin Resource Sharing (CORS) للسماح بالتحكم الدقيق في هذه الطلبات.


3. آلية Cross-Origin Resource Sharing (CORS)

CORS هي مجموعة من الرؤوس (Headers) التي يتم تبادلها بين الخادم والمتصفح لتحديد ما إذا كان الطلب Cross-Origin مسموحًا به أم لا. تعتمد هذه الآلية على قيام الخادم بإرسال رأس خاص يسمى:

  • Access-Control-Allow-Origin والذي يحدد النطاقات (الأصول) المسموح لها بالوصول إلى الموارد.

عند قيام المتصفح بإرسال طلب إلى خادم من أصل مختلف، فإنه يتوقع تلقي هذا الرأس في الاستجابة للسماح بتنفيذ الطلب. في حال عدم وجود هذا الرأس أو إذا لم يكن مطابقًا للأصل، يتم حظر الطلب.


4. طلبات Fetch API ذات الأصل المختلط

Fetch API هي واجهة برمجية حديثة في جافاسكربت تُستخدم لإجراء طلبات الشبكة بطريقة مرنة ووعدية (Promise-based). وهي تحل محل XMLHttpRequest في معظم الاستخدامات، مع توفير واجهة أكثر بساطة وقوة.

عند استخدام Fetch لإجراء طلب Cross-Origin، يتصرف المتصفح وفق قواعد CORS:

  • إذا كان الطلب بسيطًا (Simple Request)، يتم إرساله مباشرة، ويُسمح بالاستجابة فقط إذا كانت رؤوس CORS صحيحة.

  • إذا كان الطلب معقدًا (مثل استخدام طرق غير GET أو POST، أو استخدام رؤوس مخصصة)، يتم إرسال طلب تحقق مسبق Preflight Request قبل الطلب الأساسي.


5. أنواع الطلبات في CORS وكيفية التعامل معها مع Fetch

5.1 الطلبات البسيطة (Simple Requests)

تُعتبر الطلبات بسيطة إذا استوفت الشروط التالية:

  • طريقة الطلب هي GET أو POST أو HEAD.

  • الرؤوس المرسلة تقتصر على مجموعة محددة مثل Accept, Content-Type، ولا تحتوي على رؤوس مخصصة.

  • إذا كان Content-Type، فالقيمة يجب أن تكون من application/x-www-form-urlencoded, multipart/form-data أو text/plain.

عند هذه الحالة، يُرسل الطلب مباشرة بواسطة Fetch، ويعتمد قبول الاستجابة على رأس Access-Control-Allow-Origin.

5.2 الطلبات المعقدة (Preflighted Requests)

الطلبات التي لا تستوفي شروط الطلبات البسيطة تُعتبر معقدة، ويقوم المتصفح بإرسال طلب تحقق مسبق (OPTIONS request) إلى الخادم ليعرف ما إذا كانت العملية مسموحة. هذا الطلب يتضمن رؤوس مثل:

  • Access-Control-Request-Method لتحديد طريقة الطلب.

  • Access-Control-Request-Headers لتحديد الرؤوس التي سيستخدمها الطلب الأساسي.

الخادم يرد على هذا الطلب برؤوس CORS تسمح أو تمنع الطلب الأصلي.


6. التحكم في وضع الطلبات في Fetch عبر خيار mode

عند استخدام Fetch، يمكن تحديد خيار mode للتحكم في سلوك الطلب بالنسبة للأصل المختلط:

  • cors: يتيح طلبات Cross-Origin بشرط أن يوافق الخادم على ذلك عبر رؤوس CORS. هذا هو الوضع الافتراضي.

  • no-cors: يسمح بإرسال طلب Cross-Origin دون انتظار موافقة CORS، ولكن الاستجابة تكون مقيدة (لا يمكن الوصول إلى محتويات الرد).

  • same-origin: يسمح بالطلبات فقط من نفس الأصل، ويحظر الطلبات ذات الأصل المختلط.


7. كيفية استخدام Fetch مع الطلبات ذات الأصل المختلط: أمثلة عملية

7.1 مثال على طلب GET بسيط عبر Fetch مع CORS

javascript
fetch('https://api.example.com/data', { method: 'GET', mode: 'cors', // الوضع الافتراضي headers: { 'Accept': 'application/json' } }) .then(response => { if (!response.ok) { throw new Error('Network response was not ok'); } return response.json(); }) .then(data => { console.log(data); }) .catch(error => { console.error('There was a problem with the fetch operation:', error); });

في هذا المثال، يتم إرسال طلب GET إلى API خارجي، ويتم قبول الرد فقط إذا سمح الخادم بهذا عبر رأس Access-Control-Allow-Origin.

7.2 مثال على طلب POST مع إرسال بيانات JSON

javascript
fetch('https://api.example.com/submit', { method: 'POST', mode: 'cors', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name: 'محمد', age: 30 }) }) .then(response => response.json()) .then(data => console.log(data)) .catch(error => console.error('Fetch error:', error));

بما أن الطلب يحتوي على رأس مخصص (Content-Type: application/json)، فهذا يجعل الطلب معقدًا، وبذلك سيرسل المتصفح طلب تحقق مسبق OPTIONS قبل تنفيذ هذا الطلب.


8. التعامل مع الطلبات Preflight (التحقق المسبق)

عندما يتم إرسال طلب Preflight، يجب على الخادم الرد برؤوس CORS التي تسمح أو تمنع الطلب:

  • Access-Control-Allow-Methods: يحدد الطرق المسموح بها (مثل GET, POST, OPTIONS).

  • Access-Control-Allow-Headers: يحدد الرؤوس المسموح بها.

  • Access-Control-Max-Age: لتحديد مدة صلاحية الطلب المسبق.

في حال عدم توافر هذه الرؤوس بشكل صحيح، يتم رفض الطلب من قبل المتصفح.


9. إعداد الخادم لدعم CORS

من المهم جدًا لضمان نجاح الطلبات ذات الأصل المختلط، أن يدعم الخادم إعداد رؤوس CORS بشكل صحيح. مثلاً، في خادم Node.js باستخدام Express يمكن كتابة:

javascript
const express = require('express'); const app = express(); app.use((req, res, next) => { res.header('Access-Control-Allow-Origin', '*'); // يسمح لكل الأصول res.header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS'); res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization'); if (req.method === 'OPTIONS') { return res.sendStatus(204); } next(); });

مع ضبط هذه الرؤوس، يمكن للمتصفح تنفيذ طلبات Cross-Origin بسهولة.


10. التعامل مع الطلبات التي تتطلب إرسال الكوكيز مع Fetch

عند الحاجة لإرسال أو استقبال ملفات تعريف الارتباط (كوكيز) مع طلبات Cross-Origin، يجب استخدام خيار credentials في Fetch:

  • omit: لا يتم إرسال أو استقبال الكوكيز (الوضع الافتراضي في طلبات CORS).

  • same-origin: يتم إرسال الكوكيز فقط للطلبات من نفس الأصل.

  • include: يتم إرسال الكوكيز حتى في طلبات Cross-Origin.

مثال على استخدام credentials مع Fetch:

javascript
fetch('https://api.example.com/user', { method: 'GET', mode: 'cors', credentials: 'include' }) .then(response => response.json()) .then(data => console.log(data)) .catch(error => console.error(error));

وفي هذه الحالة، يجب أن يدعم الخادم هذا الطلب عبر ضبط رأس:

http
Access-Control-Allow-Credentials: true

ويجب أن يكون رأس Access-Control-Allow-Origin محددًا بدومين معين ولا يمكن استخدام *.


11. القيود الأمنية والتحديات المتعلقة بـ Cross-Origin Requests

على الرغم من أهمية CORS لتمكين التعاون بين المواقع المختلفة، إلا أن هناك عدة تحديات يجب فهمها:

  • عدم السماح بالوصول الكامل: بعض الرؤوس أو الخصائص لا يمكن تعديلها أو الوصول إليها من خلال JavaScript في طلبات Cross-Origin.

  • عدم استخدام Credentials مع Origin ‘*’: لا يمكن استخدام Access-Control-Allow-Credentials: true مع السماح لجميع الأصول *.

  • الحاجة لإعدادات خادم دقيقة: الخادم يجب أن يكون مضبوطًا بدقة لدعم CORS، وإلا سيفشل الطلب.

  • تحديات في تصحيح الأخطاء: عند رفض الطلب من المتصفح بسبب CORS، تكون رسائل الخطأ غير واضحة دائمًا مما يصعب عملية التصحيح.


12. حالات استخدام شائعة لطلبات Fetch ذات الأصل المختلط

  • التكامل مع واجهات API خارجية: كثير من المواقع تعتمد على واجهات برمجة التطبيقات (APIs) التي تُستضاف على دومينات مختلفة.

  • تحميل الموارد مثل الصور والخطوط: قد تستدعي الحاجة تحميل موارد من سيرفرات CDN أو من مواقع أخرى.

  • تنفيذ خدمات Microservices: في تطبيقات تعتمد على Microservices موزعة عبر عدة خوادم وأصول مختلفة.

  • التفاعل مع خدمات الطرف الثالث: مثل خدمات الدفع أو تحليلات المواقع.


13. نصائح عملية لتعزيز أمان ونجاح طلبات Cross-Origin

  • التأكد من ضبط رؤوس CORS على الخادم بشكل صحيح، وعدم السماح لـ * مع إرسال الكوكيز.

  • استخدام طريقة POST فقط عند الحاجة، والاعتماد على الطلبات البسيطة إن أمكن لتقليل تعقيد الطلبات.

  • تقليل الرؤوس المخصصة المرسلة مع الطلب لتجنب طلبات Preflight.

  • اختبار الطلبات في بيئة تطوير قبل الإنتاج لتفادي أخطاء CORS.

  • الاعتماد على أدوات مراقبة الشبكة في المتصفح لفهم طبيعة الطلب والاستجابة.

  • تجنب تحميل الموارد من أصول غير موثوقة للحفاظ على أمن الموقع.


14. الجدول التالي يوضح مقارنة بين أوضاع mode في Fetch وأثرها على طلبات Cross-Origin

وضع الـ mode وصف يسمح بـ Cross-Origin إمكانية قراءة الاستجابة إرسال الكوكيز
same-origin يسمح فقط بالطلبات من نفس الأصل لا نعم نعم
cors يسمح بالطلبات مع التحقق عبر CORS نعم نعم فقط إذا تم ضبط credentials
no-cors يسمح بإرسال طلب لكن الاستجابة مقيدة نعم لا لا

15. استنتاجات

التعامل مع الطلبات ذات الأصل المختلط باستخدام Fetch API يمثل جزءًا أساسيًا من تطوير تطبيقات الويب الحديثة التي تعتمد على التفاعل مع مصادر بيانات متعددة. فهم آلية CORS، وأنواع الطلبات، وكيفية إعداد الخادم والمتصفح بشكل صحيح، هو أمر ضروري لضمان نجاح هذه الطلبات بأمان وكفاءة.

تقدم Fetch API مرونة كبيرة في تنفيذ الطلبات عبر الأصول المختلفة، لكن يجب مراعاة السياسات الأمنية التي تفرضها المتصفحات، والعمل ضمن إطار CORS لضمان تبادل البيانات بشكل صحيح دون تعريض التطبيقات لمخاطر أمنية. الالتزام بضبط الرؤوس المطلوبة على الخادم، اختيار الأوضاع المناسبة في Fetch، ومعرفة كيفية التعامل مع الطلبات المعقدة والكوكيز، كلها عناصر جوهرية ترفع من مستوى أداء وأمان التطبيقات الشبكية المعاصرة.


المصادر والمراجع:

  1. Mozilla Developer Network (MDN) – Cross-Origin Resource Sharing (CORS)

    https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS

  2. Mozilla Developer Network (MDN) – Fetch API

    https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API