Fetch: запросы на другие сайты
Если мы сделаем запрос fetch на другой веб-сайт, он, вероятно, завершится неудачей.
Например, давайте попробуем запросить http://example.com :
Вызов fetch не удался, как и ожидалось.
Ключевым понятием здесь является источник (origin) – комбинация домен/порт/протокол.
Запросы на другой источник – отправленные на другой домен (или даже поддомен), или протокол, или порт – требуют специальных заголовков от удалённой стороны.
Эта политика называется «CORS»: Cross-Origin Resource Sharing («совместное использование ресурсов между разными источниками»).
Зачем нужен CORS? Экскурс в историю
CORS существует для защиты интернета от злых хакеров.
Серьёзно. Давайте сделаем краткое историческое отступление.
Многие годы скрипт с одного сайта не мог получить доступ к содержимому другого сайта.
Это простое, но могучее правило было основой интернет-безопасности. Например, хакерский скрипт с сайта hacker.com не мог получить доступ к почтовому ящику пользователя на сайте gmail.com . И люди чувствовали себя спокойно.
В то время в JavaScript не было методов для сетевых запросов. Это был «игрушечный» язык для украшения веб-страниц.
Но веб-разработчики жаждали большей власти. Чтобы обойти этот запрет и всё же получать данные с других сайтов, были придуманы разные хитрости.
Использование форм
Одним из способов общения с другим сервером была отправка туда формы <form> . Люди отправляли её в <iframe> , чтобы оставаться на текущей странице, вот так:
Таким способом было возможно сделать GET/POST запрос к другому сайту даже без сетевых методов, так как формы можно отправлять куда угодно. Но так как запрещено получать доступ к содержимому <iframe> с другого сайта, прочитать ответ было невозможно.
Если быть точным, были трюки и для этого, требующие специального кода на странице и в ифрейме, так что общение с ифреймом было технически возможно. Сейчас мы не будем вдаваться в подробности, пусть эти динозавры покоятся с миром.
Использование скриптов
Ещё один трюк заключался в использовании тега script . У него может быть любой src , с любым доменом, например <script src="http://another.com/…"> . Это даёт возможность загрузить и выполнить скрипт откуда угодно.
Если сайт, например another.com , хотел предоставить данные для такого доступа, он предоставлял так называемый «протокол JSONP» (JSON with Padding)".
Вот как он работал.
Например, нам на нашем сайте нужны данные с сайта http://another.com , скажем, погода:
Сначала, заранее, объявляем глобальную функцию для обработки данных, например gotWeather .
Затем создаём тег <script> с src="http://another.com/weather.json?callback=gotWeather" , при этом имя нашей функции – в URL-параметре callback .
Удалённый сервер с another.com должен в ответ сгенерировать скрипт, который вызывает gotWeather(. ) с данными, которые хочет передать.
Когда этот скрипт загрузится и выполнится, наша функция gotWeather получает данные.
Это работает и не нарушает безопасность, потому что обе стороны согласились передавать данные таким образом. А когда обе стороны согласны, то это определённо не хак. Всё ещё существуют сервисы, которые предоставляют такой доступ, так как это работает даже для очень старых браузеров.
Спустя некоторое время в браузерном JavaScript появились методы для сетевых запросов.
Вначале запросы на другой источник были запрещены. Но в результате долгих дискуссий было решено разрешить их делать, но для использования новых возможностей требовать разрешение сервера, выраженное в специальных заголовках.
Простые запросы
Есть два вида запросов на другой источник:
- Простые.
- Все остальные.
Простые запросы будут попроще, поэтому давайте начнём с них.
Простой запрос – это запрос, удовлетворяющий следующим условиям:
-
: GET, POST или HEAD – разрешены только:
- Accept ,
- Accept-Language ,
- Content-Language ,
- Content-Type со значением application/x-www-form-urlencoded , multipart/form-data или text/plain .
Любой другой запрос считается «непростым». Например, запрос с методом PUT или с HTTP-заголовком API-Key не соответствует условиям.
Принципиальное отличие между ними состоит в том, что «простой запрос» может быть сделан через <form> или <script> , без каких-то специальных методов.
Таким образом, даже очень старый сервер должен быть способен принять простой запрос.
В противоположность этому, запросы с нестандартными заголовками или, например, методом DELETE нельзя создать таким способом. Долгое время JavaScript не мог делать такие запросы. Поэтому старый сервер может предположить, что такие запросы поступают от привилегированного источника, «просто потому, что веб-страница неспособна их посылать».
Когда мы пытаемся сделать непростой запрос, браузер посылает специальный предварительный запрос («предзапрос», по англ. «preflight»), который спрашивает у сервера – согласен ли он принять такой непростой запрос или нет?
И, если сервер явно не даёт согласие в заголовках, непростой запрос не посылается.
Далее мы разберём конкретные детали.
CORS для простых запросов
При запросе на другой источник браузер всегда ставит «от себя» заголовок Origin .
Например, если мы запрашиваем https://anywhere.com/request со страницы https://javascript.info/page , заголовки будут такими:
Как вы можете видеть, заголовок Origin содержит именно источник (домен/протокол/порт), без пути.
Сервер может проверить Origin и, если он согласен принять такой запрос, добавить особый заголовок Access-Control-Allow-Origin к ответу. Этот заголовок должен содержать разрешённый источник (в нашем случае https://javascript.info ) или звёздочку * . Тогда ответ успешен, в противном случае возникает ошибка.
Здесь браузер играет роль доверенного посредника:
- Он гарантирует, что к запросу на другой источник добавляется правильный заголовок Origin .
- Он проверяет наличие разрешающего заголовка Access-Control-Allow-Origin в ответе и, если всё хорошо, то JavaScript получает доступ к ответу сервера, в противном случае – доступ запрещается с ошибкой.
Вот пример ответа сервера, который разрешает доступ:
Заголовки ответа
По умолчанию при запросе к другому источнику JavaScript может получить доступ только к так называемым «простым» заголовкам ответа:
- Cache-Control
- Content-Language
- Content-Length
- Content-Type
- Expires
- Last-Modified
- Pragma
При доступе к любому другому заголовку ответа будет ошибка.
Чтобы разрешить JavaScript доступ к любому другому заголовку ответа, сервер должен указать заголовок Access-Control-Expose-Headers . Он содержит список, через запятую, заголовков, которые не являются простыми, но доступ к которым разрешён.
При таком заголовке Access-Control-Expose-Headers , скрипту разрешено получить заголовки Content-Encoding и API-Key ответа.
«Непростые» запросы
Мы можем использовать любой HTTP-метод: не только GET/POST , но и PATCH , DELETE и другие.
Некоторое время назад никто не мог даже предположить, что веб-страница способна делать такие запросы. Так что могут существовать веб-сервисы, которые рассматривают нестандартный метод как сигнал: «Это не браузер». Они могут учитывать это при проверке прав доступа.
Поэтому, чтобы избежать недопониманий, браузер не делает «непростые» запросы (которые нельзя было сделать в прошлом) сразу. Перед этим он посылает предварительный запрос, спрашивая разрешения.
Предварительный запрос использует метод OPTIONS , у него нет тела, но есть три заголовка:
- Origin содержит именно источник (домен/протокол/порт), без пути.
- Access-Control-Request-Method содержит HTTP-метод «непростого» запроса.
- Access-Control-Request-Headers предоставляет разделённый запятыми список его «непростых» HTTP-заголовков.
Если сервер согласен принимать такие запросы, то он должен ответить без тела, со статусом 200 и с заголовками:
- Access-Control-Allow-Origin должен содержать разрешённый источник.
- Access-Control-Allow-Methods должен содержать разрешённые методы.
- Access-Control-Allow-Headers должен содержать список разрешённых заголовков.
- Кроме того, заголовок Access-Control-Max-Age может указывать количество секунд, на которое нужно кешировать разрешения. Так что браузеру не придётся посылать предзапрос для последующих запросов, удовлетворяющих данным разрешениям.
Давайте пошагово посмотрим, как это работает, на примере PATCH запроса (этот метод часто используется для обновления данных) на другой источник:
Этот запрос не является простым по трём причинам (достаточно одной):
- Метод PATCH
- Content-Type не один из: application/x-www-form-urlencoded , multipart/form-data , text/plain .
- Содержит «непростой» заголовок API-Key .
Шаг 1 (предзапрос)
Перед тем, как послать такой запрос, браузер самостоятельно генерирует и посылает предзапрос, который выглядит следующим образом:
- Метод: OPTIONS .
- Путь – точно такой же, как в основном запросе: /service.json .
- Особые заголовки:
- Origin – источник.
- Access-Control-Request-Method – запрашиваемый метод.
- Access-Control-Request-Headers – разделённый запятыми список «непростых» заголовков запроса.
Шаг 2 (ответ сервера на предзапрос)
Сервер должен ответить со статусом 200 и заголовками:
- Access-Control-Allow-Methods: PATCH
- Access-Control-Allow-Headers: Content-Type,API-Key .
Это разрешит будущую коммуникацию, в противном случае возникает ошибка.
Если сервер ожидает в будущем другие методы и заголовки, то он может в ответе перечислить их все сразу, разрешить заранее, например:
Теперь, когда браузер видит, что PATCH есть в Access-Control-Allow-Methods , а Content-Type,API-Key – в списке Access-Control-Allow-Headers , он посылает наш основной запрос.
Кроме того, ответ на предзапрос кешируется на время, указанное в заголовке Access-Control-Max-Age (86400 секунд, один день), так что последующие запросы не вызовут предзапрос. Они будут отосланы сразу при условии, что соответствуют закешированным разрешениям.
Шаг 3 (основной запрос)
Если предзапрос успешен, браузер делает основной запрос. Алгоритм здесь такой же, что и для простых запросов.
Основной запрос имеет заголовок Origin (потому что он идёт на другой источник):
Шаг 4 (основной ответ)
Сервер не должен забывать о добавлении Access-Control-Allow-Origin к ответу на основной запрос. Успешный предзапрос не освобождает от этого:
После этого JavaScript может прочитать ответ сервера.
Предзапрос осуществляется «за кулисами», невидимо для JavaScript.
JavaScript получает только ответ на основной запрос или ошибку, если со стороны сервера нет разрешения.
Авторизационные данные
Запрос на другой источник по умолчанию не содержит авторизационных данных (credentials), под которыми здесь понимаются куки и заголовки HTTP-аутентификации.
Это нетипично для HTTP-запросов. Обычно запрос к http://site.com сопровождается всеми куки с этого домена. Но запросы на другой источник, сделанные методами JavaScript – исключение.
Например, fetch(‘http://another.com’) не посылает никаких куки, даже тех (!), которые принадлежат домену another.com .
Потому что запрос с авторизационными данными даёт намного больше возможностей, чем без них. Если он разрешён, то это позволяет JavaScript действовать от имени пользователя и получать информацию, используя его авторизационные данные.
Действительно ли сервер настолько доверяет скрипту? Тогда он должен явно разрешить такие запросы при помощи дополнительного заголовка.
Чтобы включить отправку авторизационных данных в fetch , нам нужно добавить опцию credentials: "include" , вот так:
Теперь fetch пошлёт куки с домена another.com вместе с нашим запросом на этот сайт.
Если сервер согласен принять запрос с авторизационными данными, он должен добавить заголовок Access-Control-Allow-Credentials: true к ответу, в дополнение к Access-Control-Allow-Origin .
Пожалуйста, обратите внимание: в Access-Control-Allow-Origin запрещено использовать звёздочку * для запросов с авторизационными данными. Там должен быть именно источник, как показано выше. Это дополнительная мера безопасности, чтобы гарантировать, что сервер действительно знает, кому он доверяет делать такие запросы.
Итого
С точки зрения браузера запросы к другому источнику бывают двух видов: «простые» и все остальные.
Простые запросы должны удовлетворять следующим условиям:
- Метод: GET, POST или HEAD.
- Заголовки – мы можем установить только:
- Accept
- Accept-Language
- Content-Language
- Content-Type со значением application/x-www-form-urlencoded , multipart/form-data или text/plain .
Основное их отличие заключается в том, что простые запросы с давних времён выполнялись с использованием тегов <form> или <script> , в то время как непростые долгое время были невозможны для браузеров.
Практическая разница состоит в том, что простые запросы отправляются сразу с заголовком Origin , а для других браузер делает предварительный запрос, спрашивая разрешения.
Для простых запросов:
- → Браузер посылает заголовок Origin с источником.
- ← Для запросов без авторизационных данных (не отправляются по умолчанию) сервер должен установить:
- Access-Control-Allow-Origin в * или то же значение, что и Origin
- Access-Control-Allow-Origin в то же значение, что и Origin
- Access-Control-Allow-Credentials в true
Дополнительно, чтобы разрешить JavaScript доступ к любым заголовкам ответа, кроме Cache-Control , Content-Language , Content-Type , Expires , Last-Modified или Pragma , сервер должен перечислить разрешённые в заголовке Access-Control-Expose-Headers .
Для непростых запросов перед основным запросом отправляется предзапрос:
- → Браузер посылает запрос OPTIONS на тот же адрес с заголовками:
- Access-Control-Request-Method – содержит запрашиваемый метод,
- Access-Control-Request-Headers – перечисляет непростые запрашиваемые заголовки.
- Access-Control-Allow-Methods со списком разрешённых методов,
- Access-Control-Allow-Headers со списком разрешённых заголовков,
- Access-Control-Max-Age с количеством секунд для кеширования разрешений
Задачи
Почему нам нужен Origin?
Как вы, вероятно, знаете, существует HTTP-заголовок Referer , который обычно содержит адрес страницы, инициировавшей сетевой запрос.
Например, при запросе (fetch) http://google.com с http://javascript.info/some/url заголовки выглядят так:
Как вы можете видеть, присутствуют и Referer , и Origin .
- Почему нужен Origin , если Referer содержит даже больше информации?
- Возможно ли отсутствие Referer или Origin , или это неправильно?
Нам нужен Origin , потому что иногда Referer отсутствует. Например, когда мы запрашиваем через fetch HTTP-страницу с HTTPS (менее безопасный доступ с более безопасного), то Referer нет.
Content Security Policy (политика безопасности содержимого) может запретить отправление Referer .
Как мы увидим позже, у fetch есть опции, которые предотвращают отправку Referer и даже позволяют изменять его (в пределах того же сайта).
Согласно спецификации, Referer является необязательным HTTP-заголовком.
Именно потому что Referer ненадёжен, был изобретён Origin . Браузер гарантирует наличие правильного Origin при запросах на другой источник.
Использование JavaScript Fetch API для получения данных

Было время, когда для запросов API использовался XMLHttpRequest . В нем не было промисов, и он не позволял создавать чистый код JavaScript. В jQuery мы использовали более чистый синтаксис с jQuery.ajax() .
Сейчас JavaScript имеется собственный встроенный способ отправки запросов API. Это Fetch API, новый стандарт создания серверных запросов с промисами, также включающий много других возможностей.
В этом учебном модуле мы создадим запросы GET и POST, используя Fetch API.
Предварительные требования
Для этого обучающего модуля вам потребуется следующее:
- Последняя версия Node, установленная на вашем компьютере. Чтобы установить Node в macOS, выполните указания учебного модуля Установка Node.js и создание локальной среды разработки в macOS.
- Базовое понимание принципов программирования в JavaScript, о которых можно узнать более подробно в серии Программирование на JavaScript.
- Понимание понятия промисов в JavaScript. Прочитайте раздел «Промисы» этой статьи, чтобы узнать больше о циклах событий, обратных вызовах, промисах и асинхронном/ожидающем в JavaScript.
Шаг 1 — Введение в синтаксис Fetch API
Чтобы использовать Fetch API, вызовите метод fetch , который принимает URL API в качестве параметра:
После метода fetch() нужно включить метод промиса then() :
Метод fetch() возвращает промис. Если возвращается промис resolve , будет выполнена функция метода then() . Эта функция содержит код для обработки данных, получаемых от API.
Под методом then() следует включить метод catch() :
API, вызываемый с помощью метода fetch() , может не работать или на нем могут возникнуть ошибки. Если это произойдет, будет возвращен промис reject . Метод catch используется для обработки reject . Код метода catch() выполняется в случае возникновения ошибки при вызове выбранного API.
В целом, использование Fetch API выглядит следующим образом:
Теперь мы понимаем синтаксис использования Fetch API и можем переходить к использованию fetch() с реальным API.
Шаг 2 — Использование Fetch для получения данных от API
Следующие примеры кода основаны на Random User API. Используя API, вы получаете десять пользователей и выводите их на странице, используя Vanilla JavaScript.
Идея заключается в том, чтобы получить все данные от Random User API и вывести их в элементах списка внутри списка автора. Для начала следует создать файл HTML и добавить в него заголовок и неупорядоченный список с идентификатором authors :
Теперь добавьте теги script в конец файла HTML и используйте селектор DOM для получения ul . Используйте getElementById с аргументом authors . Помните, что authors — это идентификатор ранее созданного ul :
Создайте постоянную переменную url , в которой будет храниться URL-адрес API, который вернет десять случайных пользователей:
Теперь у нас есть ul и url , и мы можем создать функции, которые будут использоваться для создания элементов списка. Создайте функцию под названием createNode , принимающую параметр с именем element :
Впоследствии, при вызове createNode , вам нужно будет передать имя создаваемого элемента HTML.
Добавьте в функцию выражение return , возвращающее element , с помощью document.createElement() :
Также вам нужно будет создать функцию с именем append , которая принимает два параметра: parent и el :
Эта функция будет добавлять el к parent , используя document.createElement :
Теперь и createNode , и append готовы к использованию. Используя Fetch API, вызовите Random User API, добавив к fetch() аргумент url :
В вышеуказанном коде вы вызываете Fetch API и передаете URL в Random User API. После этого поступает ответ. Однако ответ вы получите не в формате JSON, а в виде объекта с серией методов, которые можно использовать в зависимости от того, что вы хотите делать с информацией. Чтобы конвертировать возвращаемый объект в формат JSON, используйте метод json() .
Добавьте метод then() , содержащий функцию с параметром resp :
Параметр resp принимает значение объекта, возвращаемого fetch(url) . Используйте метод json() , чтобы конвертировать resp в данные JSON:
При этом данные JSON все равно необходимо обработать. Добавьте еще одно выражение then() с функцией, имеющей аргумент с именем data :
Создайте в этой функции переменную с именем authors , принимающую значение data.results :
Для каждого автора в переменной authors нам нужно создать элемент списка, выводящий портрет и имя автора. Для этого отлично подходит метод map() :
Создайте в функции map переменную li , которая будет равна createNode с li (элемент HTML) в качестве аргумента:
Повторите эту процедуру, чтобы создать элемент span и элемент img :
Предлагает имя автора и портрет, идущий вместе с именем. Установите в img.src портрет автора:
Элемент span должен содержать имя и фамилию автора . Для этого можно использовать свойство innerHTML и интерполяцию строк:
Когда изображение и элемент списка созданы вместе с элементом span , вы можете использовать функцию append , которую мы ранее добавили для отображения этих элементов на странице:
Выполнив обе функции then() , вы сможете добавить функцию catch() . Эта функция поможет зарегистрировать потенциальную ошибку на консоли:
Это полный код запроса, который вы создали:
Вы только что успешно выполнили запрос GET, используя Random User API и Fetch API. На следующем шаге вы научитесь выполнять запросы POST.
Шаг 3 — Обработка запросов POST
По умолчанию Fetch использует запросы GET, но вы также можете использовать и все другие типы запросов, изменять заголовки и отправлять данные. Для этого нужно задать объект и передать его как второй аргумент функции fetch.
Прежде чем создать запрос POST, создайте данные, которые вы хотите отправить в API. Это будет объект с именем data с ключом name и значением Sammy (или вашим именем):
Обязательно добавьте постоянную переменную, хранящую ссылку на Random User API.
Поскольку это запрос POST, ее нужно будет указать явно. Создайте объект с именем fetchData :
Этот объект должен содержать три ключа: method , body и headers . Ключ method должен иметь значение ‘POST’ . Для body следует задать значение только что созданного объекта data . Для headers следует задать значение new Headers() :
Интерфейс Headers является свойством Fetch API, который позволяет выполнять различные действия с заголовками запросов и ответов HTTP. Если вы захотите узнать об этом больше, вы можете найти более подробную информацию в статье под названием Определение маршрутов и методов запросов HTTP в Express.
С этим кодом можно составлять запросы POST, используя Fetch API. Мы добавим url и fetchData как аргументы запроса fetch POST:
Функция then() будет включать код, обрабатывающий ответ, получаемый от сервера Random User API:
Есть и другая опция, позволяющая создать объект и использовать функцию fetch() . Вместо того, чтобы создавать такой объект как fetchData , вы можете использовать конструктор запросов для создания объекта запроса. Для этого нужно создать переменную с именем request :
Для переменной request следует задать значение new Request . Конструкт new Request принимает два аргумента: URL API ( url ) и объект. В объекте также должны содержаться ключи method , body и headers , как и в fetchData :
Теперь request можно использовать как единственный аргумент для fetch() , поскольку он также включает URL-адрес API:
В целом код будет выглядеть следующим образом:
Теперь вы знаете два метода создания и выполнения запросов POST с помощью Fetch API.
Заключение
Хотя Fetch API поддерживается еще не всеми браузерами, он представляет собой отличную альтернативу XMLHttpRequest. Если вы хотите узнать, как вызывать Web API с помощью React, ознакомьтесь с этой статьей по данной теме.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
How to Use Fetch in JavaScript
Fetch is a function in JavaScript used for interacting with a database and APIs (Application Programming Interface). These interactions include retrieving, uploading, editing and deleting data.

You can think of Fetch like the literal game of playing fetch with your dog:
- You throw the stick and see where it lands (Fetch the URL).
- Your dog runs after the stick and has it in their mouth to eventually bring it back (a promise to do what you want).
- Your dog brings it back to you and delivers it to you, as you would like (you receive the data and can use it however you’d like).
Fetch Requests
To understand Fetch, you must understand the four types of requests that Fetch provides us with:
- GET requests — used for retrieving or “fetching” data.
- POST requests — used for uploading or adding data.
- PATCH / PUT requests — used for editing data that is already in the database.
- DELETE requests — used for deleting a piece of data.
Fetch accepts two arguments. The first is a string URL and the second is an optional argument that includes information regarding the request you want to make to the database or API. If you are only trying to get data from an API, you do not need to use the second argument.
Promises
If you enter the above code into your console, you’ll get a promise. In fact, a successful Fetch request will always return a promise. A great way to think of a promise is as an IOU. This IOU guarantees that something will be returned to you. Along with a promise, comes a response, but how do you turn that response into usable data for applications?
You do this with a .then statement, that converts the data into something readable and digestible with JSON. JSON or JavaScript Object Notation is the format for how you transmit data through web applications.
Here is an example of our Fetch request example with its first .then statement:
The first .then statement is always the same! It parses out the returned promise in JSON.
When you call .json() on a promise, in return, you get yet another promise. This second promise is imperative because it allows you to see and use the data you return in a way that you see fit.
In the below example the data from the API is being logged to the console via the the second promise in the Fetch request.
Now Lets Go Through the 4 Different FETCH Requests You Will Be Making
For the purpose of this blog and simplicity, I created a simple movie database in my db.json file in VSCode and ran it on localhost:3000 using the command json-server —watchdb.json .
This is what my database looks like:
GET Request
GET requests are used for retrieving or “fetching” data from an API:
For GET requests, a second argument is not necessary.
POST Request
POST requests are used for uploading or adding data to an API:
configObject is an object I created to be the second argument. In this object, I have three key-value pairs:
- The first is the request I am making to the database or backend. In this case, it’s a POST request.
- Next is the headers, which contains information about the data being sent. Content-type states what format the data will be sent in. Accept simply means what format you’ll accept the data in.
- The last key-value pair represents the information about the attributes needed to create a movie object in a stringified form.
After creating configObject , I added it in as the second argument in the Fetch request. The Fetch request will return the data, it will accept the promise with a .then response and parse the information into JSON. Then .json() will return another promise that you can work with how you see fit.
PATCH Request
PATCH / PUT requests are used for editing data that is already in the database.
Instead of creating a configObject like in the POST request, I wanted to show you the syntax of what it looks like if you decide to insert that data inside of the Fetch request. This is in lieu of abstracting it in a variable and including it in the second argument after the link.
Since I am updating information, I am going to be using a PATCH method. For PATCH requests the link is dynamic, you make a link dynamic by using back-ticks («). I did this because I want the PATCH request to be applicable to any movie with any Id in our database and to do this I need to interpolate the $
. I also changed the body section of the request. I did this because in my application I only want certain attributes to be changeable. In this case it’s the image_url and the rating . I omitted the title and release_date since those attributes of a movie usually do not change.
DELETE Request
DELETE requests are used for deleting a piece of data:
Like the PATCH request, I put back-ticks around the link so that I can interpolate the $
. Since I am just deleting data and I don’t expect to get any information back from the API, I don’t need to include headers or a body . Don’t fret if this all seems like a lot of information at first. It takes time and practice to wrap your ahead around Fetch. Don’t forget that the documentation is out there and accessible on the internet at all times. I have found the MDN Web Docs on using Fetch very helpful:
Using Fetch
The Fetch API provides a JavaScript interface for accessing and manipulating parts of the HTTP pipeline, such as…
That being said, when you are starting out try not to copy and paste from the documentation. Instead write everything out on your own, while looking at the documentation. This will help you better understand what you are writing and will engrain the syntax in your memory.
Прощай, XMLHttpRequest!
fetch() позволяет вам делать запросы, схожие с XMLHttpRequest (XHR). Основное отличие заключается в том, что Fetch API использует Promises (Обещания), которые позволяют использовать более простое и чистое API, избегать катастрофического количества callback’ов и необходимости помнить API для XMLHttpRequest.
Fetch API стал доступен пользователям вместе с Service Worker’ами в global скоупе в Chrome 40, однако уже в версии 42 он станет доступен в скоупе window. Разумеется, для всех остальных браузеров, которые пока ещё не поддерживают fetch существует полифил от GitHub, который доступен уже сегодня.
Простой Fetch запрос
Давайте начнём со сравнения простого примера, реализованного с XMLHttpRequest и fetch . Всё, что мы будем делать в этом примере — сделаем запрос на URL, получим ответ и распарсим его как JSON.
XMLHttpRequest
Пример с XMLHttpRequest потребует от нас установить два обработчика событий на success и error , а так же вызвать два метода: open() и send() . Пример из MDN документации:
Fetch
Наш fetch запрос будет выглядеть так:
Первым делом мы проверяем статус ответа и проверяем, успешно ли выполнился запрос (ожидаем 200 статус). Если всё хорошо, то парсим ответ как JSON.
Ответом fetch() является Stream-объект. Это означает, что в результате вызова метода json() мы получим Promise, т.к. чтение из подобного объекта является асинхронным.
Метаданные ответа
В предыдущем примере мы изучили, как можно проверить статус объекта ответа и конвентировать сам ответ в JSON. Остальные метаданные, к которым вы возможно получить доступ (например, заголовки), приведены ниже:
Типы ответа
Когда мы делаем fetch-запрос, ответу будет дан тип «basic», «cors» или «opaque». Эти «типы» указывают на то, с какого ресурса пришли данные и могут быть использованы для того, чтобы определить процесс обработки данных.
Когда запрос сделан на ресурс, находящимся на том же origin (имеется ввиду, что запрос выполняется в рамках одного сайта. прим. пер.), ответ будет содержать тип «базовый» и для такого запроса не будет никаких ограничений.
Если запрос сделан с одного origin’а на другой (кроссдоменный запрос), который, в свою очередь, вернул CORS заголовки, тогда типом будет являться «cors». Объекты с типами «cors» и «basic» почти идентичны, однако «cors» несколько ограничивает метаданные, к которым может быть получен доступ до «Cache-Control», «Content-Language», «Content-Type», «Expires», «Last-Modified», и «Pragma».
Что касается «opaque» — то он приходит в случаях, когда выполняется CORS запрос, но удаленный ресурс не возвращает CORS заголовки. Данный тип запроса не предоставляет доступ данным или заголовку статуса, поэтому мы не имеем возможности судить о результате выполнения запроса. В рамках текущей имплементации fetch() не представляется возможности выполнять CORS запросы из скоупа window, и вот здесь написано почему. Эта функциональность должна быть добавлена, как только Cache API станет доступным из объекта window.
Вы можете определить ожидаемый режим запроса, тем самым фильтруя результаты запросов с неподходящим типом. Режим запроса может быть установлен на следующий:
— “same-origin” успешно выполняется только для запросов на тот же самый origin, все остальные запросы будут отклонены.
— “cors” работает так же, как «same-origin» + добавляет возможность создавать запросы к сторонним сайтам, если они возвращают соответствующие CORS заголовки.
— “cors-with-forced-preflight” работает так же, как «cors», но перед запросом всегда отсылает тестовый запрос на проверку.
— “no-cors” используется, когда необходимо выполнить запрос к origin, который не отсылает CORS заголовки и результатом выполнения является объект с типом «opaque». Как говорилось выше, в данный момент это невозможно в скоупе window.Чтобы определить режим запроса, добавьте объект опций вторым параметром к запросу и установите «mode» в этом объекте:
Цепочки Promises
Одной из прекрасных особенностей Promise’ов является возможность группировать их в цепочки. Если говорить о них в скоупе fetch() , то они позволяют нам «шарить» логику между запросами.
Если вы работаете с JSON API, вам потребуется проверить статус и распарсить JSON для каждого ответа. Вы можете упростить свой код, определив парсинг статуса и JSON как раздельные функции, которые вернут Promise’ы. Вам останется подумать только об обработке самих данных и, разумеется, исключений.
Мы определяем функцию, которая проверяет response.status и возвращает результат: Promise.resolve() или Promise.reject() . Это первый вызванный метод в нашей цепочке, и если он успешно завершается( Promise.resolve() ), то вызывается следующий за ним метод — fetch() , который, в свою очередь, опять возвращает Promise от response.json() . После этого вызова, в случае удачного выполнения, у нас будет готовый JSON объект. Если парсинг провалится, Promise будет отменен и сработает условие возникновения исключения.
Но самое лучшее здесь — это возможность переиспользовать такой код для всех fetch-запросов в приложении. Такой код проще поддерживать, читать и тестировать.
POST запрос
Уже давно никого не удивишь необходимостью использовать POST метод с передачей параметров в «теле» запроса для работы с API.
Чтобы осуществить такой запрос, мы должны указать соответствующие параметры в объекте настроек fetch() :Посылаем учётные данные через Fetch-запрос
Если вы хотите отправить запрос с каким-либо учётными данными (например, с cookie), вам следует установить `credentials` в опциях запроса на «include»:
Могу ли я отменить fetch() запрос?
В настоящий момент это невозможно, но это активно обсуждается на GitHubСуществует ли полифил?
ДаПочему «no-cors» реализован для service workers, но не для window?
Это было сделано из соображений безопасности. Подробнее можно ознакомиться здесь.