מבוא ל-fetch()

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

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

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

מקור

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

בקשת אחזור בסיסית

דוגמה להטמעה באמצעות 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 עם הגבלות על מה שאפשר לראות בתגובה.

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

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

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

  • same-origin מצליח רק לבקשות לנכסים באותו מקור, ודחה את כל הבקשות האחרות.
  • cors מאפשר בקשות לנכסים באותו מקור ובמקורות אחרים שמחזירים את כותרות ה-CORS המתאימות.
  • 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 בפונקציות נפרדות שמחזירות הבטחות (promises), ולהשתמש בבקשת האחזור כדי לטפל רק בנתונים הסופיים ובמקרה השגיאה.

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 שעבר ניתוח. אם הניתוח נכשל, ה-Promise נדחה והצהרת ה-catch מתבצעת.

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

בקשת 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'
})