מבוא ל-fetch()

ה-API (שליפה) מגיע לאובייקט החלון ומחפש להחליף את ה-XHRs

מאט גאונט

So long XMLHttpRequest

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

תמיכה בדפדפן

  • 42
  • 14
  • 39
  • 10.1

מקור

אחזור API זמין בהיקף הגלובלי של Service Worker מאז Chrome 40, אבל הוא יופעל בהיקף החלון ב-Chrome 42. יש גם Polyfill by GitHub אחזור למדי שבו תוכלו להשתמש היום.

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

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

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

XMLHttpRequest

צריך להגדיר ל-XMLHttpRequest שני מאזינים שיכולים להתמודד עם מקרי הצלחה ושגיאות וקריאה ל-open() ול-send(). דוגמה ממסמכי MDN.

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

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

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

שליפה

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

fetch('./api/some.json')
    .then(
    function(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(function(err) {
    console.log('Fetch Error :-S', err);
    });

נתחיל בבדיקה שסטטוס התגובה הוא 200 לפני ניתוח התגובה כ-JSON.

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

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

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

fetch('users.json').then(function(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 כמעט זהות, אבל התגובה cors מגבילה את הכותרות שניתן להציג ל-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(function(response) {
    return response.text();
    })
    .then(function(text) {
    console.log('Request successful', text);
    })
    .catch(function(error) {
    log('Request failed', error)
    });

שרשרת הבטחות

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

אם אתם עובדים עם JSON API, תצטרכו לבדוק את הסטטוס ולנתח את קובץ ה-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(function(data) {
    console.log('Request succeeded with JSON response', data);
    }).catch(function(error) {
    console.log('Request failed', error);
    });

אנחנו מגדירים את הפונקציה status שבודקת את response.status, ומחזירה את התוצאה של Promise.resolve() או של Promise.reject(), שמחזירה הבטחה מתוקנת או נדחתה. זו השיטה הראשונה שנקראת בשרשרת fetch() שלנו. אם היא נפתרה, אנחנו קוראים ל-method json() שמחזירה שוב Promise מהקריאה response.json(). אחרי זה יש לנו אובייקט של ה-JSON המנותח. אם הניתוח נכשל, ההבטחה נדחית והצהרת ה-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(function (data) {
    console.log('Request succeeded with JSON response', data);
    })
    .catch(function (error) {
    console.log('Request failed', error);
    });

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

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

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

שאלות נפוצות

איך מבטלים בקשת אחזור() ?

כרגע אין אפשרות לבטל אחזור, אבל מדברים על זה ב-GitHub. H/T @jaffathe איכות קישור זה.

האם יש polyfill?

יש ב-GitHub מילוי רב-פעמי לאחזור. ה-H/T @Nexii על ההערה הזאת.

למה הערך "no-cors" נתמך ב-service worker אבל לא בחלון?

הסיבה לכך היא בעיית אבטחה. כאן אפשר לקבל מידע נוסף.