اذهب إلى المحتوى

استعمالات متقدمة للواجهة البرمجية Fetch في جافاسكربت


ابراهيم الخضور

لقد أخذنا فكرةً لا بأس بها عن fetch في المقالات السابقة من هذه السلسلة (بدءًا من مقال إرسال البيانات واستلامها عبر الشبكة وحتى مقال استخدام Fetch مع الطلبات ذات الأصل المختلط Cross-Origin)، والآن لنلق نظرةً على بقية مكوّنات الواجهة البرمجية لنغطي كل إمكاناتها.

اقتباس

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

إليك قائمةً كاملةً بكل خيارت fetch الممكنة مع قيمها الافتراضية (وضعنا البدائل في تعليقات):

let promise = fetch(url, {
  method: "GET", // POST, PUT, DELETE, etc.
  headers: {
    // the content type header value is usually auto-set
    // depending on the request body
    "Content-Type": "text/plain;charset=UTF-8"
  },
  body: undefined // string, FormData, Blob, BufferSource, or URLSearchParams
  referrer: "about:client", // or "" to send no Referer header,
  // or an url from the current origin
  referrerPolicy: "no-referrer-when-downgrade", // no-referrer, origin, same-origin...
  mode: "cors", // same-origin, no-cors
  credentials: "same-origin", // omit, include
  cache: "default", // no-store, reload, no-cache, force-cache, or only-if-cached
  redirect: "follow", // manual, error
  integrity: "", // a hash, like "sha256-abcdef1234567890"
  keepalive: false, // true
  signal: undefined, // AbortController to abort request
  window: window // null
});

لقد غطينا المفاهيم method وheaders وbody في مقال استخدام Fetch، كما غطينا signal في مقال إيقاف تنفيذ Fetch، وسنتعرف الآن على بقية الإمكانات.

خيارا المحيل referrer وسياسة المحيل referrerPolicy

يتحكم هذان الخياران بكيفية ضبط fetch للترويسة Referrer، وهي إحدى ترويسات HTTP، وتُضبط تلقائيًا لتحتوي على عنوان الصفحة التي ولّدت الطلب، ولا تعَد هامةً في معظم الأحيان، وقد يكون من المنطقي أحيانًا إزالتها أو تقصيرها لأسباب تتعلق بالأمان.

يسمح الخيار referrer بتسمية أي مُحيل، وهو الصفحة أو الرابط الذي أحالك إلى الصفحة الحالية التي تعمل عليها، أو إزالته على أن يشترك بالأصل مع الصفحة الحالية، وإذا لم ترغب بإرسال أي محيل فأسند إليه نصًا فارغًا:

fetch('/page', {
  referrer: "" //  لا توجد توريسة محيل     
});

ولوضع عنوان مورد آخر من الأصل ذاته:

fetch('/page', {
  // https://javascript.info بفرض أننا في 
  // نستطيع ضبط أي ترويسة محيل، لكن ضمن الأصل الحالي 
  referrer: "https://javascript.info/anotherpage"
});

يضبط الخيار referrerPolicy بعض القواعد العامة للمُحيل Referer.

تنقسم الطلبات إلى ثلاث مجموعات، هي الآتية:

  1. الطلب إلى مورد من الأصل ذاته.
  2. الطلب إلى مورد من أصل مختلف.
  3. الطلب من بروتوكول HTTPS إلى بروتوكول HTTP: أي من بروتوكول النقل الآمن إلى غير الآمن.

يدل الخيار referrerPolicy المتصفح على القواعد الخاصة باستخدام المُحيل في كل مجموعة من الطلبات، ولا يسمح بضبط القيمة الدقيقة للمحيل.

ستجد جميع القيم الممكنة في توصيف سياسة المحيل:

  • no-referrer-when-downgrade: قيمتها الافتراضية "full"، حيث تُرسل قيمة المحُيل دومًا عدا الحالة التي يُرسَل فيها الطلب من HTTPS إلى HTTP (إلى بروتوكول أقل أمانًا).
  • no-referrer: لا يُرسَل المُحيل.
  • origin: يُرسل الأصل فقط ضمن المُحيل وليس عنوان الصفحة المُحيلة الكامل، أي يُرسل العنوان على الشكل http://site.com وليس على الشكل http://site.com/path.
  • origin-when-cross-origin: يُرسل العنوان الكامل للمحيل إلى المواقع ذات الأصل المشترك، بينما يُرسَل الأصل فقط إلى المواقع ذات الأصل المختلط.
  • same-origin: يُرسل المُحيل كاملًا إلى المواقع التي تنتمي إلى نفس الأصل، ولايرُسل أبدًا إلى المواقع ذات الأصول المختلطة.
  • strict-origin: يُرسل الأصل فقط وليس المُحيل كاملًا في الطلبات من HTTPS إلى HTTP.
  • strict-origin-when-cross-origin: يُرسل المُحيل كاملًا إلى المواقع التي تنتمي إلى نفس الأصل، ويُرسل الأصل فقط إلى المواقع ذات الأصول المختلطة، عدا الحالة التي يُرسَل فيها الطلب من HTTPS إلى HTTP، فلا يُرسل شيء.
  • unsafe-url: يُرسل عنوان المُحيل كاملًا، حتى في الحالة التي يُرسَل فيها الطلب من HTTPS إلى HTTP.

يوضح الجدول التالي جميع الخيارات:

القيمة إلى نفس الأصل إلى أصل مختلف HTTPS→HTTP
"no-referrer" - - -
no-referrer-when-downgrade أو "" وهي القيمة الافتراضية كاملًا كاملًا -
"origin" الأصل الأصل الأصل
"origin-when-cross-origin" كاملًا الأصل الأصل
"same-origin" كاملًا - -
"strict-origin" الأصل الأصل -
"strict-origin-when-cross-origin" كاملًا الأصل -
"unsafe-url" كاملًا كاملًا كاملًا

لنفترض وجود صفحة بصلاحيات مدير، ولا ينبغي كشف عنوانها خارج نطاق الموقع، لذا فعند إرسال fetch، فسترسَل الترويسة Referer افتراضيًا مع عنوان صفحتنا كاملًا، عدا الحالة التي يُرسَل فيها الطلب من HTTPS إلى HTTP. حيث لا توجد أي ترويسة Referer، فإذا كان العنوان هو Referer: https://javascript.info/admin/secret/paths مثلًا، وأردنا إرسال الأصل فقط وليس العنوان الكامل، فيمكن أن نرسل الخيار التالي:

fetch('https://another.com/page', {
  // ...
  referrerPolicy: "origin-when-cross-origin" // Referer: https://javascript.info
});

يمكن وضع الخيار السابق لكل استدعاءات fetch، كما يمكن أيضًا دمجه في مكتبة JavaScript التي نستخدمها في مشروعنا، والتي تنفّذ كل الطلبات التي تستخدم fetch، ويقتصر الفرق الوحيد بينه وبين الخيار الافتراضي في أنه يرسِل الجزء الأصلي من عنوان الموقع المُحيل، مثلًا: https://javascript.info ولا يُرسل المسار الكلي، وسنحصل على العنوان الكامل في الطلبات المُرسَلة إلى مواقع من نفس الأصل، فربما تكون مفيدةً لأغراض التنقيح.

اقتباس

لا تُستخدم سياسة المحيل مع fetch فقط. فمن الممكن ضبط السياسة الافتراضية للصفحة كاملةً باستخدام Referrer-Policy في ترويسة HTTP، أو لرابط مفرد باستخدام <"a rel="noreferrer>.

الخيار mode

ويمثل هذا الخيار الحارس الذي يمنع الطلبات ذات الأصل المختلط التي تحدث فجأةً.

  • cors: وهي القيمة الافتراضية، وتسمح بالطلبات ذات الأصل المختلط كما ورد في فصل استخدام Fetch في الطلبات ذات الأصل المختلط.
  • same-origin: يمنع استخدام الطلبات ذات الأصل المختلط.
  • no-cors: يسمح فقط لطلبات الأصل المختلط الآمنة.

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

الخيار credentials

ويحدد ما إذا كان على fetch إرسال ملفات تعريف الارتباط cookies، وترويسات استيثاق HTTP مع الطلب.

  • same-origin: وهي القيمة الافتراضية، لا تُرسل الثبوتيات مع الطلبات ذات الأصول المختلطة.
  • include: تُرسل الثبوتيات دومًا، ونحتاج إلى الترويسة Access-Control-Allow-Credentials من الخادم ذي الأصل المختلط لتتمكن جافا سكربت من الوصول إلى الاستجابة، وقد شرحنا ذلك في فصل استخدام Fetch في الطلبات ذات الأصل المختلط.
  • omit: لا تُرسل الثبوتيات أبدًا، حتى للطلبات من الأصل نفسه.

الخيار cache

تستخدم طلبات fetch افتراضيًا ذاكرة HTTP المؤقتة المعيارية HTTP-cache، فهي تحترم الترويستين Expires وCache-Control، وترسل الترويسة If-Modified-Since تمامًا كما تفعله طلبات HTTP النظامية.

يسمح الخيار cache بتجاهل "HTTP-cache" أو يضبط استخدامه:

  • default: تستخدم fetch ترويسات وقواعد "HTTP-cache" المعيارية.
  • no-store: يتجاهل الطلب قواعد "HTTP-cache" كليًا، وتصبح هذه القيمة افتراضيةً عند إرسال إحدى الترويسات التالية: If-Modified-Since أو If-None-Match أو If-Unmodified-Since أو If-Match أو If-Range.
  • reload: لا يأخذ النتيجة من "HTTP-cache" -إن وجدت-، بل ينشر محتويات الذاكرة المؤقتة مع الاستجابة، إذا سمحت ترويسات الاستجابة بذلك.
  • no-cache: يُنشئ طلبًا شرطيًا عند وجود استجابة مخزنة في الذاكرة المؤقتة، وطلبًا عاديًا في غير تلك الحالة، وينشر "HTTP-cache" مع الاستجابة.
  • force-cache: يستخدم الاستجابة الموجودة في "HTTP-cache" حتى لو كانت قديمة، وسينشئ طلب HTTP نظاميًا إذا لم تحتوي على استجابة، كما سيسلك الطلب السلوك الطبيعي.
  • only-if-cached: يستخدم الاستجابة الموجودة في "HTTP-cache" حتى لو كانت قديمةً، وسيرمي خطأً إذا لم تحتوي على استجابة، وتعمل فقط مع القيمة same-origin للخيار mode.

الخيار redirect

تخضع fetch بكل شفافية لإعادة التوجيه "HTTP-redirect" مثل الحالتين 301 (النقل النهائي لمحتوى) و302 (موجود ولكن يفضل الانتقال إلى العنوان الجديد).

  • follow: وهي القيمة الافتراضية، ويخضع الطلب عندها لحالات إعادة التوجيه.
  • error: يرمي خطأً عند محاولة إعادة توجيه الطلب.
  • manual: يسمح بالتعامل مع إعادة توجيه الطلب يدويًا، وسنحصل عندها على كائن استجابة خاص من النوع "response.type="opaqueredirect، وتكون قيمة خاصية الحالة response.status صفرًا، وكذلك قيم أغلب خصائصه.

الخيار integrity

يسمح هذا الخيار بالتحقق من مطابقة الاستجابة للقيم الاختبارية Checksum المحددة مسبقًا، كما هو محدد في التوصيفات. وتُدعم دوال "hash" التالية: SHA-256 وSHA-384 وSHA-512، كما قد تتطلب بعض الاعتماديات وفقًا للمتصفح، فإذا كنا بصدد تنزيل ملف مثلًا، ونعلم أنّ القيمة الاختبارية له وفق SHA-256 هي "abcdef"، والتي ستكون أطول في الواقع، فيمكننا وضعها قيمةً للخيار integrity بالشكل التالي:

fetch('http://site.com/file', {
  integrity: 'sha256-abcdef'
});

ستحسب fetch قيمة SHA-256 بنفسها وتوازنها مع القيمة التي وضعناها، وسترمي خطأً عند عدم تطابق القيمتين.

الخيار keepalive

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

window.onunload = function() {
  fetch('/analytics', {
    method: 'POST',
    body: "statistics",
    keepalive: true
  });
};

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

  • لا يمكن إرسال أحجام بالميجابايت: لأن الحد الأعلى لحجم جسم الطلب مع خيار keepalive هو 64 كيلوبابت.
  • إذا أردنا جمع إحصائيات كثيرةً عن الزائر، فلا بدّ من إرسالها بانتظام ضمن حزم متتالية، لكي لا تبقى الكثير من المعلومات التي لم ترسل بعد عند تنفيذ الطلب الأخير مع الحدث onunload.
  • تطبق هذه التقييدات على كل الطلبات التي تحمل الخيار keepalive معًا، أي يمكن تنفيذ عدة طلبات من هذا النوع في الوقت نفسه، لكن يجب ألا يتجاوز مجموع أحجام أجسام هذه الطلبات حد 64 كيلوبايت.
  • لا يمكن التعامل مع استجابة الخادم عند إزالة المستند، لذا سينجح استخدام fetch في مثالنا بوجود keepalive، لكن بالطبع لن تُنفَّذ الدوال اللاحقة.
  • لن تظهر المشاكل في أغلب الأحيان عند إرسال بيانات مثل الإحصائيات، لأنّ الخادم سيقبل هذه البيانات وسيعيد غالبًا استجابةً فارغةً لطلبات مثل هذه.

ترجمة -وبتصرف- للفصل Fetch: API من سلسلة The Modern JavaScript Tutorial.

اقرأ أيضًا


تفاعل الأعضاء

أفضل التعليقات

لا توجد أية تعليقات بعد



انضم إلى النقاش

يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.

زائر
أضف تعليق

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   جرى استعادة المحتوى السابق..   امسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • أضف...