מבוא ל-fetch()

באמצעות fetch() אפשר ליצור בקשות רשת הדומות ל-XMLHttpRequest (XHR). ההבדל העיקרי הוא שב-Fetch API נעשה שימוש ב-Promises, שיש לו ממשק API פשוט יותר שעוזר להימנע מהקריאות החוזרות המורכבות ב-XMLHttpRequest API.

תמיכה בדפדפנים

  • Chrome: 42.
  • Edge: 14.
  • Firefox: 39.
  • Safari: 10.1.

מקור

אם זו הפעם הראשונה שאתם משתמשים ב-Promises, כדאי לקרוא את המאמר Introduction to JavaScript Promises.

דוגמה להטמעה באמצעות XMLHttpRequest ואז באמצעות fetch. אנחנו רוצים לבקש כתובת URL, לקבל תשובה ולנתח אותה כ-JSON.

XMLHttpRequest

ל-XMLHttpRequest נדרשים שני מאזינים כדי לטפל במקרים של הצלחה ושגיאה, וקריאה ל-open() ול-send(). דוגמה ממסמכי MDN.

function reqListener () {
  const data = JSON.parse(this.responseText);
  console.log(data);
}

function reqError (err) {
  console.log('Fetch Error :-S', err);
}

const oReq = new XMLHttpRequest();
oReq.onload = reqListener;
oReq.onerror = reqError;
oReq.open('get', './api/some.json', true);
oReq.send();

הבא

בקשת האחזור שלנו נראית כך:

fetch('./api/some.json')
  .then(response => {
    if (response.status !== 200) {
      console.log(`Looks like there was a problem. Status Code: ${response.status}`);

      return;
    }

    // Examine the text in the response
    response.json().then(function(data) {
      console.log(data);
    });
  })
  .catch(err => {
    console.log('Fetch Error :-S', err);
  });

כדי לבצע את אותה עבודה כמו בדוגמה של ה-XHR, צריך רק קריאה אחת לבקשת fetch(). כדי לעבד את התגובה, קודם בודקים שסטטוס התגובה הוא 200, ואז מנתחים את התגובה כ-JSON. התגובה לבקשה fetch() היא אובייקט Stream, כלומר אחרי שאנחנו קוראים ל-method‏ json(), מוחזר Promise. השידור מתבצע באופן אסינכרוני.

מטא-נתונים של התשובה

בדוגמה הקודמת רואים את הסטטוס של האובייקט Response, ואיך לנתח את התשובה כ-JSON. כך תטפלו במטא-נתונים שאולי תרצו לגשת אליהם, כמו כותרות:

fetch('users.json').then(response => {
  console.log(response.headers.get('Content-Type'));
  console.log(response.headers.get('Date'));

  console.log(response.status);
  console.log(response.statusText);
  console.log(response.type);
  console.log(response.url);
});

סוגי תגובה

כששולחים בקשת אחזור, הערך של response.type בתגובה הוא basic,‏ cors או opaque. הערכים האלה של types מצביעים על המקור של המשאב, וניתן להשתמש בהם כדי לקבוע איך לטפל באובייקט התגובה.

כשהדפדפן מבקש משאב באותו מקור, התשובה תהיה מסוג basic עם הגבלות על המידע שתוכלו לראות מהתשובה.

אם נשלחה בקשה למשאב ממקור אחר, והמקור הזה מחזיר כותרות COR, הסוג הוא cors. התשובות של cors דומות לתשובות של basic, אבל הן מגבילות את הכותרות שאפשר להציג ל-Cache-Control, Content-Language, Content-Type, Expires, Last-Modified ו-Pragma.

תגובות opaque מגיעות ממקור אחר שלא מחזיר כותרות CORS. בתגובה אטומה, לא נוכל לקרוא את הנתונים שהוחזרו או להציג את סטטוס הבקשה, כלומר לא תוכלו לבדוק אם הבקשה הצליחה.

אפשר להגדיר מצב לבקשת אחזור, כך שרק סוגי בקשות מסוימים יטופלו. אלה המצבים שאפשר להגדיר:

  • same-origin מצליח רק בבקשות לנכסים מאותו המקור ודוחה את כל הבקשות האחרות.
  • הפקודה cors מאפשרת בקשות לנכסים מאותו המקור ובמקורות אחרים שמחזירים את כותרות ה-COR המתאימות.
  • cors-with-forced-preflight מבצע בדיקה לפני ההפעלה לפני שליחת בקשה.
  • no-cors מיועד לשלוח בקשות למקורות אחרים שאין להם כותרות CORS, וכתוצאה מכך לקבל תשובה מסוג opaque. עם זאת, כפי שצוין, אי אפשר לעשות זאת כרגע ברמת החלון הגלובלית.

כדי להגדיר את המצב, מוסיפים אובייקט אפשרויות כפרמטר השני בבקשה של fetch ומגדירים את המצב באובייקט הזה:

fetch('http://some-site.com/cors-enabled/some.json', {mode: 'cors'})
  .then(response => response.text())
  .then(text => {
    console.log('Request successful', text);
  })
  .catch(error => {
    log('Request failed', error)
  });

שרשרת הבטחות

אחת מהתכונות הגדולות של הבטחות היא היכולת לשרשר אותן זו לזו. ב-fetch(), כך אפשר לשתף לוגיקה בין בקשות אחזור.

אם אתם עובדים עם API בפורמט JSON, עליכם לבדוק את הסטטוס ולנתח את ה-JSON של כל תשובה. כדי לפשט את הקוד, אפשר להגדיר את הסטטוס ואת ניתוח ה-JSON בפונקציות נפרדות שמחזירות הבטחות, ולהשתמש בבקשת האחזור כדי לטפל רק בנתונים הסופיים ובתרחיש השגיאה.

function status (response) {
  if (response.status >= 200 && response.status < 300) {
    return Promise.resolve(response)
  } else {
    return Promise.reject(new Error(response.statusText))
  }
}

function json (response) {
  return response.json()
}

fetch('users.json')
  .then(status)
  .then(json)
  .then(data => {
    console.log('Request succeeded with JSON response', data);
  }).catch(error => {
    console.log('Request failed', error);
  });

בדוגמה הזו מוגדרת פונקציית status שבודקת את response.status ומחזירה Promise שהתקבל כ-Promise.resolve() או Promise שנדחה כ-Promise.reject(). זו השיטה הראשונה שנקראת בשרשרת fetch().

אם ה-Promise מתקבל, הסקריפט קורא ל-method‏ json(), שמחזיר Promise שני מהקריאה ל-response.json() ויוצר אובייקט שמכיל את ה-JSON שעבר ניתוח. אם הניתוח נכשל, ההבטחה נדחית והצהרת השליפה תפעל.

המבנה הזה מאפשר לשתף את הלוגיקה בכל בקשות האחזור, וכך קל יותר לתחזק, לקרוא ולבדוק את הקוד.

בקשת POST

לפעמים אפליקציית אינטרנט צריכה לקרוא ל-API באמצעות שיטת POST ולכלול כמה פרמטרים בגוף הבקשה. כדי לעשות זאת, מגדירים את הפרמטרים method ו-body באפשרויות fetch():

fetch(url, {
    method: 'post',
    headers: {
      "Content-type": "application/x-www-form-urlencoded; charset=UTF-8"
    },
    body: 'foo=bar&lorem=ipsum'
  })
  .then(json)
  .then(data => {
    console.log('Request succeeded with JSON response', data);
  })
  .catch(error => {
    console.log('Request failed', error);
  });

שליחת פרטי כניסה באמצעות בקשת אחזור

כדי לשלוח בקשת אחזור עם פרטי כניסה כמו קובצי cookie, צריך להגדיר את הערך credentials של הבקשה ל-"include":

fetch(url, {
  credentials: 'include'
})