Node fetch что это
Перейти к содержимому

Node fetch что это

  • автор:

Fetch

JavaScript может отправлять сетевые запросы на сервер и подгружать новую информацию по мере необходимости.

Например, мы можем использовать сетевой запрос, чтобы:

  • Отправить заказ,
  • Загрузить информацию о пользователе,
  • Запросить последние обновления с сервера,
  • …и т.п.

Для сетевых запросов из JavaScript есть широко известный термин «AJAX» (аббревиатура от Asynchronous JavaScript And XML). XML мы использовать не обязаны, просто термин старый, поэтому в нём есть это слово. Возможно, вы его уже где-то слышали.

Есть несколько способов делать сетевые запросы и получать информацию с сервера.

Метод fetch() — современный и очень мощный, поэтому начнём с него. Он не поддерживается старыми (можно использовать полифил), но поддерживается всеми современными браузерами.

let promise = fetch(url, [options])
  • url – URL для отправки запроса.
  • options – дополнительные параметры: метод, заголовки и так далее.

Без options это простой GET-запрос, скачивающий содержимое по адресу url .

Браузер сразу же начинает запрос и возвращает промис, который внешний код использует для получения результата.

Процесс получения ответа обычно происходит в два этапа.

Во-первых, promise выполняется с объектом встроенного класса Response в качестве результата, как только сервер пришлёт заголовки ответа.

На этом этапе мы можем проверить статус HTTP-запроса и определить, выполнился ли он успешно, а также посмотреть заголовки, но пока без тела ответа.

Промис завершается с ошибкой, если fetch не смог выполнить HTTP-запрос, например при ошибке сети или если нет такого сайта. HTTP-статусы 404 и 500 не являются ошибкой.

Мы можем увидеть HTTP-статус в свойствах ответа:

  • status – код статуса HTTP-запроса, например 200.
  • ok – логическое значение: будет true , если код HTTP-статуса в диапазоне 200-299.
let response = await fetch(url); if (response.ok) < // если HTTP-статус в диапазоне 200-299 // получаем тело ответа (см. про этот метод ниже) let json = await response.json(); >else

Во-вторых, для получения тела ответа нам нужно использовать дополнительный вызов метода.

Response предоставляет несколько методов, основанных на промисах, для доступа к телу ответа в различных форматах:

  • response.text() – читает ответ и возвращает как обычный текст,
  • response.json() – декодирует ответ в формате JSON,
  • response.formData() – возвращает ответ как объект FormData (разберём его в следующей главе),
  • response.blob() – возвращает объект как Blob (бинарные данные с типом),
  • response.arrayBuffer() – возвращает ответ как ArrayBuffer (низкоуровневое представление бинарных данных),
  • помимо этого, response.body – это объект ReadableStream, с помощью которого можно считывать тело запроса по частям. Мы рассмотрим и такой пример несколько позже.

Например, получим JSON-объект с последними коммитами из репозитория на GitHub:

let url = 'https://api.github.com/repos/javascript-tutorial/en.javascript.info/commits'; let response = await fetch(url); let commits = await response.json(); // читаем ответ в формате JSON alert(commits[0].author.login);

То же самое без await , с использованием промисов:

fetch('https://api.github.com/repos/javascript-tutorial/en.javascript.info/commits') .then(response => response.json()) .then(commits => alert(commits[0].author.login));

Для получения ответа в виде текста используем await response.text() вместо .json() :

let response = await fetch('https://api.github.com/repos/javascript-tutorial/en.javascript.info/commits'); let text = await response.text(); // прочитать тело ответа как текст alert(text.slice(0, 80) + '. ');

В качестве примера работы с бинарными данными, давайте запросим и выведем на экран логотип спецификации «fetch» (см. главу Blob, чтобы узнать про операции с Blob ):

let response = await fetch('/article/fetch/logo-fetch.svg'); let blob = await response.blob(); // скачиваем как Blob-объект // создаём let img = document.createElement('img'); img.style = 'position:fixed;top:10px;left:10px;width:100px'; document.body.append(img); // выводим на экран img.src = URL.createObjectURL(blob); setTimeout(() => < // прячем через три секунды img.remove(); URL.revokeObjectURL(img.src); >, 3000);

Мы можем выбрать только один метод чтения ответа.

Если мы уже получили ответ с response.text() , тогда response.json() не сработает, так как данные уже были обработаны.

let text = await response.text(); // тело ответа обработано let parsed = await response.json(); // ошибка (данные уже были обработаны)

Заголовки ответа

Заголовки ответа хранятся в похожем на Map объекте response.headers .

Это не совсем Map , но мы можем использовать такие же методы, как с Map , чтобы получить заголовок по его имени или перебрать заголовки в цикле:

let response = await fetch('https://api.github.com/repos/javascript-tutorial/en.javascript.info/commits'); // получить один заголовок alert(response.headers.get('Content-Type')); // application/json; charset=utf-8 // перебрать все заголовки for (let [key, value] of response.headers) < alert(`$= $`); >

Заголовки запроса

Для установки заголовка запроса в fetch мы можем использовать опцию headers . Она содержит объект с исходящими заголовками, например:

let response = fetch(protectedUrl, < headers: < Authentication: 'secret' >>);

Есть список запрещённых HTTP-заголовков, которые мы не можем установить:

  • Accept-Charset , Accept-Encoding
  • Access-Control-Request-Headers
  • Access-Control-Request-Method
  • Connection
  • Content-Length
  • Cookie , Cookie2
  • Date
  • DNT
  • Expect
  • Host
  • Keep-Alive
  • Origin
  • Referer
  • TE
  • Trailer
  • Transfer-Encoding
  • Upgrade
  • Via
  • Proxy-*
  • Sec-*

Эти заголовки обеспечивают достоверность данных и корректную работу протокола HTTP, поэтому они контролируются исключительно браузером.

POST-запросы

Для отправки POST -запроса или запроса с другим методом, нам необходимо использовать fetch параметры:

  • method – HTTP метод, например POST ,
  • body – тело запроса, одно из списка:
    • строка (например, в формате JSON),
    • объект FormData для отправки данных как form/multipart ,
    • Blob / BufferSource для отправки бинарных данных,
    • URLSearchParams для отправки данных в кодировке x-www-form-urlencoded , используется редко.

    Чаще всего используется JSON.

    Например, этот код отправляет объект user как JSON:

    let user = < name: 'John', surname: 'Smith' >; let response = await fetch('/article/fetch/post/user', < method: 'POST', headers: < 'Content-Type': 'application/json;charset=utf-8' >, body: JSON.stringify(user) >); let result = await response.json(); alert(result.message);

    Заметим, что так как тело запроса body – строка, то заголовок Content-Type по умолчанию будет text/plain;charset=UTF-8 .

    Но, так как мы посылаем JSON, то используем параметр headers для отправки вместо этого application/json , правильный Content-Type для JSON.

    Отправка изображения

    Мы можем отправить бинарные данные при помощи fetch , используя объекты Blob или BufferSource .

    В этом примере есть элемент , на котором мы можем рисовать движением мыши. При нажатии на кнопку «Отправить» изображение отправляется на сервер:

        

    Заметим, что здесь нам не нужно вручную устанавливать заголовок Content-Type , потому что объект Blob имеет встроенный тип ( image/png , заданный в toBlob ). При отправке объектов Blob он автоматически становится значением Content-Type .

    Функция submit() может быть переписана без async/await , например, так:

    function submit() < canvasElem.toBlob(function(blob) < fetch('/article/fetch/post/image', < method: 'POST', body: blob >) .then(response => response.json()) .then(result => alert(JSON.stringify(result, null, 2))) >, 'image/png'); >

    Итого

    Типичный запрос с помощью fetch состоит из двух операторов await :

    let response = await fetch(url, options); // завершается с заголовками ответа let result = await response.json(); // читать тело ответа в формате JSON
    fetch(url, options) .then(response => response.json()) .then(result => /* обрабатываем результат */)
    • response.status – HTTP-код ответа,
    • response.ok – true , если статус ответа в диапазоне 200-299.
    • response.headers – похожий на Map объект с HTTP-заголовками.

    Методы для получения тела ответа:

    • response.text() – возвращает ответ как обычный текст,
    • response.json() – декодирует ответ в формате JSON,
    • response.formData() – возвращает ответ как объект FormData (кодировка form/multipart, см. следующую главу),
    • response.blob() – возвращает объект как Blob (бинарные данные с типом),
    • response.arrayBuffer() – возвращает ответ как ArrayBuffer (низкоуровневые бинарные данные),

    Опции fetch , которые мы изучили на данный момент:

    • method – HTTP-метод,
    • headers – объект с запрашиваемыми заголовками (не все заголовки разрешены),
    • body – данные для отправки (тело запроса) в виде текста, FormData , BufferSource , Blob или UrlSearchParams .

    В следующих главах мы рассмотрим больше параметров и вариантов использования fetch .

    Задачи

    Получите данные о пользователях GitHub

    Создайте асинхронную функцию getUsers(names) , которая получает на вход массив логинов пользователей GitHub, запрашивает у GitHub информацию о них и возвращает массив объектов-пользователей.

    Информация о пользователе GitHub с логином USERNAME доступна по ссылке: https://api.github.com/users/USERNAME .

    В песочнице есть тестовый пример.

    1. На каждого пользователя должен приходиться один запрос fetch .
    2. Запросы не должны ожидать завершения друг друга. Надо, чтобы данные приходили как можно быстрее.
    3. Если какой-то запрос завершается ошибкой или оказалось, что данных о запрашиваемом пользователе нет, то функция должна возвращать null в массиве результатов.

    Чтобы получить сведения о пользователе, нам нужно вызвать fetch(‘https://api.github.com/users/USERNAME’) .

    Если ответ приходит cо статусом 200 , то вызываем метод .json() , чтобы прочитать JS-объект.

    А если запрос завершается ошибкой или код статуса в ответе отличен от 200, то мы просто возвращаем null в массиве результатов.

    async function getUsers(names) < let jobs = []; for(let name of names) < let job = fetch(`https://api.github.com/users/$`).then( successResponse => < if (successResponse.status != 200) < return null; >else < return successResponse.json(); >>, failResponse => < return null; >); jobs.push(job); > let results = await Promise.all(jobs); return results; >

    Пожалуйста, обратите внимание: вызов .then прикреплён к fetch , чтобы, когда ответ получен, сразу начинать считывание данных с помощью .json() , не дожидаясь завершения других запросов.

    Если бы мы использовали await Promise.all(names.map(name => fetch(. ))) и вызывали бы .json() на результатах запросов, то пришлось бы ждать, пока завершатся все из них. Вызывая .json() сразу после каждого fetch , мы добились того, что считывание присланных по каждому запросу данных происходит независимо от других запросов.

    Это пример того, как относительно низкоуровневое Promise API может быть полезным, даже если мы в основном используем async/await в коде.

    Выполнение HTTP-запросов в Node.js с помощью node-fetch

    Веб-приложению часто требуется взаимодействовать с веб-серверами для получения различных ресурсов. Возможно, вам потребуется извлечь данные или отправить данные на внешний веб-сервер или API.

    Используя клиентский JavaScript, этого можно достичь с помощью API fetch и функции window.fetch() . В NodeJS несколько пакетов / библиотек могут достичь одного и того же результата. Один из них — node-fetch .

    node-fetch — это небольшой модуль, который позволяет нам использовать функцию fetch() в NodeJS, с функциональностью, очень похожей на нативный window.fetch() , но с некоторыми отличиями:

    node-fetch/v3-LIMITS.md at master · node-fetch/node-fetch · GitHub

    A light-weight module that brings Fetch API to Node.js — node-fetch/node-fetch

    https://github.com/node-fetch/node-fetch/blob/master/docs/v3-LIMITS.md

    Начало работы с node-fetch

    Чтобы использовать node-fetch в своем проекте, перейдите в каталог проекта и выполните:

    npm install node-fetch 

    Чтобы использовать модуль в коде, используйте:

    var fetch = require('node-fetch'); 

    Как упоминалось ранее, функция fetch() в модуле node-fetch ведет себя очень похоже на window.fetch() . Его сигнатура:

    fetch(url[, options]) 

    Параметр url просто прямой URL ресурса который мы хотим запросить. Это должен быть абсолютный URL, иначе функция выдаст ошибку. Необязательный параметр options используется, когда мы хотим использовать что-либо, кроме простого GET запроса, но мы поговорим об этом более подробно позже.

    Функция возвращает объект Response , содержащий полезные функции и информацию об ответе HTTP, например:

    1. text() — возвращает тело ответа в виде строки
    2. json() — анализирует тело ответа на объект JSON и выдает ошибку, если тело не может быть проанализировано
    3. status и statusText — содержат информацию о коде статуса HTTP
    4. ok — равно true , если status это код состояния 2xx (успешный запрос)
    5. headers — объект, содержащий заголовки ответа, к определенному заголовку можно получить доступ с помощью функции get() .

    Отправка запросов GET с использованием node-fetch

    Есть два распространенных случая получения данных с веб-сервера. Возможно, вы захотите получить текст с веб-сервера, целую веб-страницу или данные с помощью REST API. Пакет node-fetch позволяет все это сделать.

    Создайте каталог для вашего проекта и инициализируйте проект Node с настройками по умолчанию:

    npm init -y 

    Это создаст файл package.json в каталоге. Затем установите node-fetch , как показано выше, и добавьте файл index.js .

    Получение текста или веб-страниц

    Сделаем простой GET запрос на главную страницу Google:

    var fetch = require('node-fetch'); fetch('https://google.com') .then(res => res.text()) .then(text => console.log(text)) 

    В приведенном выше коде мы загружаем модуль node-fetch , а затем получаем домашнюю страницу Google. Единственный параметр, который мы добавили в функцию fetch() — это URL-адрес сервера, к которому мы отправляем HTTP-запрос. Поскольку node-fetch основан на обещаниях, мы объединяем в цепочку несколько функций .then() , чтобы помочь нам управлять ответом и данными из нашего запроса.

    В этой строке мы ждем ответа от веб-сервера Google и конвертируем его в текстовый формат:

    .then(res => res.text()) 

    Здесь ждем результата предыдущего преобразования и выводим его в консоль:

    .then(text => console.log(text)) 

    Если мы запустим приведенный выше код из консоли:

    node index.js 

    Мы получим всю HTML-разметку домашней страницы Google, зарегистрированную в консоли:

    Получение данных JSON из REST API

    Другой распространенный вариант использования модуля node-fetch — получение данных с помощью REST API.

    Мы получим поддельные данные пользователя из REST API JSONPlaceholder. Как и раньше, функция fetch() принимает URL-адрес сервера и ожидает ответа.

    Давайте посмотрим, как это работает:

    var fetch = require('node-fetch'); fetch('https://jsonplaceholder.typicode.com/users') .then(res => res.json()) .then(json => < console.log("First user in the array:") console.log(json[0]) console.log("Name of the first user in the array:") console.log(json[0].name) >) 

    Тело ответа HTTP содержит данные в формате JSON, а именно массив, содержащий информацию о пользователях. Имея это в виду, мы использовали функцию .json() , и это позволило нам легко получить доступ к отдельным элементам и их полям.

    Запуск этой программы даст нам:

    First element in the array: < id: 1, name: 'Leanne Graham', username: 'Bret', email: 'Sincere@april.biz', address: < street: 'Kulas Light', suite: 'Apt. 556', city: 'Gwenborough', zipcode: '92998-3874', geo: < lat: '-37.3159', lng: '81.1496' >>, phone: '1-770-736-8031 x56442', website: 'hildegard.org', company: < name: 'Romaguera-Crona', catchPhrase: 'Multi-layered client-server neural-net', bs: 'harness real-time e-markets' >> Name of the first person in the array: Leanne Graham 

    Мы также могли напечатать весь JSON, возвращенный res.json() .

    Отправка запросов POST с помощью node-fetch

    Мы также можем использовать эту функцию fetch() для публикации данных вместо их получения. Как мы упоминали ранее, что fetch() позволяет добавить дополнительный параметр для выполнения POST запросов к веб-серверу. Без этого необязательного параметра наш запрос по умолчанию является запросом GET .

    Давайте добавим новый элемент в список задач JSONPlaceholder. Давайте добавим в этот список новый элемент для пользователя, у которого id равно 123 . Сначала нам нужно создать объект todo , а затем преобразовать его в JSON при добавлении в поле body :

    var fetch = require('node-fetch'); var todo = < userId: 123, title: "loren impsum doloris", completed: false >fetch('https://jsonplaceholder.typicode.com/todos', < method: 'POST', body: JSON.stringify(todo), headers: < 'Content-Type': 'application/json' >>) .then(res => res.json()) .then(json => console.log(json))

    Процесс очень похож на отправку GET запроса. Мы вызвали функцию fetch() с соответствующим URL-адресом и установили необходимые параметры, используя необязательный параметр функции fetch() . Раньше мы преобразовывали наш объект в строку в формате JSON перед его отправкой на веб-сервер с помощью JSON.stringify() . Затем, как и при получении данных, мы ждали ответа, конвертировали его в JSON и выводили на консоль.

    Запуск кода дает нам результат:

    Обработка исключений и ошибок

    Наши запросы иногда могут завершаться неудачно по разным причинам — из-за ошибки в функции fetch() , проблем с Интернетом, внутренних ошибок сервера и других. Нам нужен способ справиться с этими ситуациями или, по крайней мере, уметь видеть, что они произошли.

    Мы можем обрабатывать исключения выполнения catch() , добавляя их в конец цепочки обещаний. Давайте добавим простую функцию catch() в нашу программу выше:

    var fetch = require('node-fetch'); var todo = < userId: 123, title: "loren impsum doloris", completed: false >fetch('https://jsonplaceholder.typicode.com/todos', < method: 'POST', body: JSON.stringify(todo), headers: < 'Content-Type': 'application/json' >>) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.log(err)) 

    В идеале вы не должны просто игнорировать и распечатывать ошибки, а вместо этого иметь систему для их обработки.

    Мы должны помнить, что если наш ответ имеет код состояния 3xx / 4xx / 5xx, запрос либо не выполнен, либо клиенту необходимо предпринять дополнительные шаги.

    А именно, коды состояния HTTP 3xx указывают на то, что клиенту необходимо предпринять дополнительные шаги, коды 4xx указывают на недопустимый запрос, а коды 5xx указывают на ошибки сервера. Все эти коды состояния говорят нам, что наш запрос не был успешным с практической точки зрения.

    catch() не будет регистрировать ни один из этих случаев, потому что связь с сервером прошла успешно, т.е. мы сделали запрос и получили ответ успешно. Это означает, что нам необходимо предпринять дополнительные шаги, чтобы убедиться, что мы охватили ситуацию, когда связь клиент-сервер была успешной, но мы не получили ни одного из успешных (2xx) кодов состояния HTTP.

    Распространенный способ убедиться, что неудачные запросы вызывают ошибку, — это создать функцию, которая проверяет HTTP-статус ответа от сервера. В этой функции, если код состояния не указывает на успех, мы можем выдать ошибку и catch() поймать ее.

    Мы можем использовать ранее упомянутое поле ok объекта Response , которое равно true , если код состояния равен 2xx.

    Посмотрим, как это работает:

    var fetch = require('node-fetch'); function checkResponseStatus(res) < if(res.ok)< return res >else < throw new Error(`The HTTP status of the reponse: $($)`) > > fetch('https://jsonplaceholder.typicode.com/MissingResource') .then(checkResponseStatus) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.log(err)) 

    Мы использовали функцию в начале цепочки обещаний (перед синтаксическим анализом тела ответа), чтобы узнать, столкнулись ли мы с проблемой. Вместо этого вы также можете выдать настраиваемую ошибку.

    Опять же, у вас должна быть стратегия обработки подобных ошибок, а не просто вывод на консоль.

    Если все прошло, как ожидалось, и код состояния указывает на успех, программа продолжит работу, как и раньше.

    Вывод

    Выполнение запросов к веб-серверам — обычная задача веб-разработки, и в этой статье мы увидели, как мы можем сделать это эффективно, используя библиотеку node-fetch , которая делает API-интерфейс fetch из браузера совместимым с NodeJS.

    В дополнение к этому мы также рассмотрели, как обрабатывать ошибки, которые могут возникнуть с HTTP-запросами.

    Хватит использовать Fetch API в JavaScript

    Перед разработчиком нередко встает вопрос, какие использовать пакеты, инструменты и способы реализации задуманного. К примеру, каким способом стоит создавать HTTP-запросы? В данной статье я расскажу о библиотеке Axios и Fetch API, попутно продемонстрировав их отличия.

    О чем вообще речь?

    Когда требуется осуществлять информационный обмен с серверами через HTTP, мы отправляем запросы и получаем в ответ данные — этот процесс называется AJAX (асинхронный JavaScript и XML). Два самых известных ключевых слова, которые любой фронтенд-разработчик держит в арсенале для выполнения данной операции — это Axios и Fetch.

    Axios

    Axios — это библиотека JS, используемая для отправки HTTP-запросов из Node.js либо браузера. В документации указаны следующие ее возможности:

    • Использование XMLHttpRequests из браузера;
    • Выполнение HTTP запросов из Node.js;
    • Поддержка Promise API;
    • Перехват запроса и ответа;
    • Преобразование данных запроса и ответа;
    • Отмена запросов;
    • Автоматическое преобразование для данных в формате JSON;
    • Клиентская поддержка защиты от XSRF (межсайтовая подделка запросов).

    Поскольку библиотека Axios не является нативной для JavaScript API, ее необходимо импортировать в проект. Поэтапную инструкцию установки можете найти здесь.

    Fetch API

    Fetch API предоставляет интерфейс для получения ресурсов из браузера. Согласно документации, Fetch дает общее определение объектов для запроса и ответа. Этот API также использует промисы и предоставляет глобальный метод fetch() . Данный метод требует в качестве необходимого аргумента только путь к нужному ресурсу. В ответ он возвращает промис, который разрешает ответ на этот запрос, независимо от того, успешен он был или нет.

    Совместимость

    Вот список ведущих браузеров, поддерживающих эти инструменты:

    • Axios — Chrome, Firefox, Safari, Opera, Edge, IE.
    • Fetch — Chrome, Firefox, Safari, Opera, Edge.

    Можно смело сказать, что Axios отлично поддерживается в старых версиях браузеров, а также в IE, при том что Fetch API нет.

    Безопасность

    Axios является менее уязвимым, поскольку предоставляет защиту от XSRF на клиентской стороне. Как правило, такие атаки происходят в ситуациях, когда HTTP-запрос совершается между сайтами с целью выдать злоумышленника за истинного пользователя. Более подробно об этом виде атак можно прочесть в Википедии.

    Синтаксис

    При сравнении Axios и Fetch важно отметить, что они сильно отличаются в своем синтаксисе. Возьмем, к примеру, метод GET :

    const url = 'https://jsonplaceholder.typicode.com/posts';// Axios
    axios.get(url)
    .then(response => console.log(response));
    // Fetch
    fetch(url)
    .then(response => response.json())
    .then(data => console.log(data));

    Если вы с этими инструментами еще не сталкивались, то наверняка поинтересуетесь, почему в случае Fetch использован метод .json() . По факту то же самое происходит в Axios внутренне при выполнении автоматической сериализации в JSON после разрешения запроса.

    Прерывание запросов и ответов

    HTTP-перехватчики отлично подходят для проверки HTTP-запросов, передаваемых от клиента серверу. Они хороши для изменения запросов/ответов до момента их начала/получения. В Fetch нет способа перехвата HTTP-запросов, но есть возможность обойти это ограничение переопределением глобального метода fetch() и созданием его собственного перехватчика.

    При этом в Axios уже есть встроенный перехватчик, который выполняется даже до промисов .then или .catch . Продемонстрирую:

    axios.interceptors.request.use( 
    config => // Здесь можно выполнить нужные действия с данными запроса
    console.log('A request has been sent!');
    return config;
    >, (error) => return Promise.reject(error);
    >
    );
    axios.interceptors.response.use(
    config => // Здесь можно выполнить нужные действия с данными ответа
    console.log('A response has been received!');
    return config;
    >, (error) => return Promise.reject(error);
    >
    );
    axios.get('https://jsonplaceholder.typicode.com/posts')
    .then(response => console.log(response));

    Таймаут и прогресс запроса

    Одно из действительно крутых преимуществ Axios в том, что Fetch не предоставляет среди своих настроек timeout , определяющий временное ограничение, по истечении которого запрос отменяется. Таймаут является возможной опцией, передаваемой в объект config и измеряется в миллисекундах. Важно помнить, что таймаут относится к ответу, а не соединению. Например:

    axios.get( 
    'https://jsonplaceholder.typicode.com/posts',
    timeout: 10
    >
    )
    .then(response => console.log(response));

    Однако есть способ создать таймаут и с помощью Fetch API:

    const controller = new AbortController();
    const timeoutValue = 10000;
    const timeout = setTimeout(() => controller.abort(), timeoutValue);
    fetch('https://jsonplaceholder.typicode.com/posts', < signal: controller.signal >)
    .then(response => response.json())
    .then(data => console.log(data));

    Что касается прогресса, то и здесь есть очередной повод предпочесть Axios, поскольку он предоставляет более простой способ узнать о прогрессе запроса. Одной из опциональных настроек, передаваемой для этого в объект конфигурации, является OnUploadProgress .

    Обработка ошибок

    С помощью Axios обрабатывать ошибки проще, поскольку плохие ответы автоматически отвергаются в отличие от Fetch, где даже 404 или 500 ошибки все равно обрабатываются. В случае, когда сервер возвращает код “500 Internal Server Error”, Fetch обработает его аналогично коду “200 OK”. Для решения этой проблемы можно использовать флаг “ok”:

    fetch('https://official-joke-api.appspot.com/jokes/ten/hehe') 
    .then(response => if (!response.ok) throw Error(response.statusText)
    >
    return response.json()
    >)
    .then(data => console.log(data)
    >)
    .catch(error => console.error(error))

    В случае же Axios простой пример обработки ошибки будет таким:

    axios 
    .get('https://official-joke-api.appspot.com/jokes/ten/hehe')
    .then(response => console.log("response", response)
    >)
    .catch(error => console.log(error))

    Заключение

    Из рассмотренного материала очевидно, что Axios проще использовать, чем Fetch. Однако при этом также стоит учесть, что Axios является внешним пакетом, который потребуется включать в проект дополнительно. Fetch же изначально встроен в браузер, то есть дополнительного места не займет. Лично я предпочитаю использовать Axios, но это нельзя назвать правилом, так что окончательный выбор за вами. Делайте его с умом!

    Fetch API

    Давайте рассмотрим оставшуюся часть API, чтобы охватить все возможности.

    На заметку:

    Заметим: большинство этих возможностей используются редко. Вы можете пропустить эту главу и, несмотря на это, нормально использовать fetch .

    Тем не менее, полезно знать, что вообще может fetch , чтобы, когда появится необходимость, вернуться и прочитать конкретные детали.

    Нижеследующий список – это все возможные опции для fetch с соответствующими значениями по умолчанию (в комментариях указаны альтернативные значения):

    let promise = fetch(url, < method: "GET", // POST, PUT, DELETE, etc. headers: < // значение этого заголовка обычно ставится автоматически, // в зависимости от тела запроса "Content-Type": "text/plain;charset=UTF-8" >, body: undefined, // string, FormData, Blob, BufferSource или URLSearchParams referrer: "about:client", // или "" для того, чтобы не послать заголовок Referer, // или URL с текущего источника referrerPolicy: "strict-origin-when-cross-origin", // 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 или only-if-cached redirect: "follow", // manual, error integrity: "", // контрольная сумма, например "sha256-abcdef1234567890" keepalive: false, // true signal: undefined, // AbortController, чтобы прервать запрос window: window // null >);

    Довольно-таки внушительный список, не так ли?

    В главе Fetch мы разобрали параметры method , headers и body .

    Опция signal разъяснена в главе в Fetch: прерывание запроса.

    Теперь давайте пройдёмся по оставшимся возможностям.

    referrer, referrerPolicy

    Данные опции определяют, как fetch устанавливает HTTP-заголовок Referer .

    Обычно этот заголовок ставится автоматически и содержит URL-адрес страницы, с которой пришёл запрос. В большинстве случаев он совсем неважен, в некоторых случаях, с целью большей безопасности, имеет смысл убрать или укоротить его.

    Опция referrer позволяет установить любой Referer в пределах текущего источника или же убрать его.

    Чтобы не отправлять Referer , нужно указать значением пустую строку:

    fetch('/page', < referrer: "" // не ставить заголовок Referer >);

    Для того, чтобы установить другой URL-адрес (должен быть с текущего источника):

    fetch('/page', < // предположим, что мы находимся на странице https://javascript.info // мы можем установить любое значение Referer при условии, что оно принадлежит текущему источнику referrer: "https://javascript.info/anotherpage" >);

    Опция referrerPolicy устанавливает общие правила для Referer .

    Выделяется 3 типа запросов:

    1. Запрос на тот же источник.
    2. Запрос на другой источник.
    3. Запрос с HTTPS to HTTP (с безопасного протокола на небезопасный).

    В отличие от настройки referrer , которая позволяет задать точное значение Referer , настройка referrerPolicy сообщает браузеру общие правила, что делать для каждого типа запроса.

    • «strict-origin-when-cross-origin» – значение по умолчанию: для «same-origin» отправлять полный Referer , для «cross-origin» отправлять только «origin» , если только это не HTTPS→HTTP запрос, тогда не отправлять ничего.
    • «no-referrer-when-downgrade» – всегда отправлять полный Referer , за исключением случаев, когда мы отправляем запрос с HTTPS на HTTP (на менее безопасный протокол).
    • «no-referrer» – никогда не отправлять Referer .
    • «origin» – отправлять в Referer только текущий источник, а не полный URL-адрес страницы, например, посылать только http://site.com вместо http://site.com/path .
    • «origin-when-cross-origin» – отправлять полный Referer для запросов в пределах текущего источника, но для запросов на другой источник отправлять только сам источник (как выше).
    • «same-origin» – отправлять полный Referer для запросов в пределах текущего источника, а для запросов на другой источник не отправлять его вообще.
    • «strict-origin» – отправлять только значение источника, не отправлять Referer для HTTPS→HTTP запросов.
    • «unsafe-url» – всегда отправлять полный URL-адрес в Referer , даже при запросах HTTPS→HTTP .

    Вот таблица со всеми комбинациями:

    Значение На тот же источник На другой источник HTTPS→HTTP
    «no-referrer»
    «no-referrer-when-downgrade» full full
    «origin» origin origin origin
    «origin-when-cross-origin» full origin origin
    «same-origin» full
    «strict-origin» origin origin
    «strict-origin-when-cross-origin» или «» (по умолчанию) full origin
    «unsafe-url» full full full

    Допустим, у нас есть админка со структурой URL, которая не должна стать известной снаружи сайта.

    Если мы отправляем запрос fetch , то по умолчанию он всегда отправляет заголовок Referer с полным URL-адресом нашей админки (исключение – это когда мы делаем запрос от HTTPS в HTTP, в таком случае Referer не будет отправляться).

    Например, Referer: https://javascript.info/admin/secret/paths .

    Если мы хотим, чтобы другие сайты получали только источник, но не URL-путь, это сделает такая настройка:

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

    Мы можем поставить её во все вызовы fetch , возможно, интегрировать в JavaScript-библиотеку нашего проекта, которая делает все запросы и внутри использует fetch .

    Единственным отличием в поведении будет то, что для всех запросов на другой источник fetch будет посылать только источник в заголовке Referer (например, https://javascript.info , без пути). А для запросов на наш источник мы продолжим получать полный Referer (это может быть полезно для отладки).

    Политика установки Referer (Referrer Policy) – не только для fetch

    Политика установки Referer, описанная в спецификации Referrer Policy, существует не только для fetch , она более глобальная.

    В частности, можно поставить политику по умолчанию для всей страницы, используя HTTP-заголовок Referrer-Policy , или на уровне ссылки .

    mode

    Опция mode – это защита от нечаянной отправки запроса на другой источник:

    • «cors» – стоит по умолчанию, позволяет делать такие запросы так, как описано в Fetch: запросы на другие сайты,
    • «same-origin» – запросы на другой источник запрещены,
    • «no-cors» – разрешены только простые запросы на другой источник.

    Эта опция может пригодиться, если URL-адрес для fetch приходит от третьей стороны, и нам нужен своего рода «глобальный выключатель» для запросов на другие источники.

    credentials

    Опция credentials указывает, должен ли fetch отправлять куки и авторизационные заголовки HTTP вместе с запросом.

    • «same-origin» – стоит по умолчанию, не отправлять для запросов на другой источник,
    • «include» – отправлять всегда, но при этом необходим заголовок Access-Control-Allow-Credentials в ответе от сервера, чтобы JavaScript получил доступ к ответу сервера, об этом говорилось в главе Fetch: запросы на другие сайты,
    • «omit» – не отправлять ни при каких обстоятельствах, даже для запросов, сделанных в пределах текущего источника.

    cache

    По умолчанию fetch делает запросы, используя стандартное HTTP-кеширование. То есть, учитывается заголовки Expires , Cache-Control , отправляется If-Modified-Since и так далее. Так же, как и обычные HTTP-запросы.

    Настройка cache позволяет игнорировать HTTP-кеш или же настроить его использование:

    • «default» – fetch будет использовать стандартные правила и заголовки HTTP кеширования,
    • «no-store» – полностью игнорировать HTTP-кеш, этот режим становится режимом по умолчанию, если присутствуют такие заголовки как If-Modified-Since , If-None-Match , If-Unmodified-Since , If-Match , или If-Range ,
    • «reload» – не брать результат из HTTP-кеша (даже при его присутствии), но сохранить ответ в кеше (если это дозволено заголовками ответа);
    • «no-cache» – в случае, если существует кешированный ответ – создать условный запрос, в противном же случае – обычный запрос. Сохранить ответ в HTTP-кеше,
    • «force-cache» – использовать ответ из HTTP-кеша, даже если он устаревший. Если же ответ в HTTP-кеше отсутствует, сделать обычный HTTP-запрос, действовать как обычно,
    • «only-if-cached» – использовать ответ из HTTP-кеша, даже если он устаревший. Если же ответ в HTTP-кеше отсутствует, то выдаётся ошибка. Это работает, только когда mode установлен в «same-origin» .

    redirect

    Обычно fetch прозрачно следует HTTP-редиректам, таким как 301, 302 и так далее.

    Это можно поменять при помощи опции redirect :

    • «follow» – стоит по умолчанию, следовать HTTP-редиректам,
    • «error» – ошибка в случае HTTP-редиректа,
    • «manual» – не следовать HTTP-редиректу, но установить адрес редиректа в response.url , а response.redirected будет иметь значение true , чтобы мы могли сделать перенаправление на новый адрес вручную.

    integrity

    Опция integrity позволяет проверить, соответствует ли ответ известной заранее контрольной сумме.

    Как описано в спецификации, поддерживаемыми хеш-функциями являются SHA-256, SHA-384 и SHA-512. В зависимости от браузера, могут быть и другие.

    Например, мы скачиваем файл, и мы точно знаем, что его контрольная сумма по алгоритму SHA-256 равна «abcdef» (разумеется, настоящая контрольная сумма будет длиннее).

    Мы можем добавить это в настройку integrity вот так:

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

    Затем fetch самостоятельно вычислит SHA-256 и сравнит его с нашей строкой. В случае несоответствия будет ошибка.

    keepalive

    Опция keepalive указывает на то, что запрос может «пережить» страницу, которая его отправила.

    Например, мы собираем статистические данные о том, как посетитель ведёт себя на нашей странице (на что он кликает, части страницы, которые он просматривает), для анализа и улучшения интерфейса.

    Когда посетитель покидает нашу страницу – мы хотим сохранить собранные данные на нашем сервере.

    Для этого мы можем использовать событие window.onunload :

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

    Обычно, когда документ выгружается, все связанные с ним сетевые запросы прерываются. Но настройка keepalive указывает браузеру выполнять запрос в фоновом режиме даже после того, как пользователь покидает страницу. Поэтому эта опция обязательна, чтобы такой запрос удался.

    У неё есть ряд ограничений:

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *