Выполнение 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, например:
- text() — возвращает тело ответа в виде строки
- json() — анализирует тело ответа на объект JSON и выдает ошибку, если тело не может быть проанализировано
- status и statusText — содержат информацию о коде статуса HTTP
- ok — равно true , если status это код состояния 2xx (успешный запрос)
- 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
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 .
В песочнице есть тестовый пример.
- На каждого пользователя должен приходиться один запрос fetch .
- Запросы не должны ожидать завершения друг друга. Надо, чтобы данные приходили как можно быстрее.
- Если какой-то запрос завершается ошибкой или оказалось, что данных о запрашиваемом пользователе нет, то функция должна возвращать 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 в коде.
Зачем нужен nodejs если есть fetch?
Здравия всем.
Несмотря на большой объём литературы из интернета никак не могу уловить основную концепцию организации работы веб-сайта (приложения). Или, хотя бы общепринятую. Поэтому не могу приступить к изучению бекенда, хотя для себя давно решил, что это будет только javascript с его экосистемой.
Один из основных нерешённых вопросов, организация открытой и закрытой частей приложения. Сейчас организовал свой сайт axdrv.ru, и упёрся всё в тот же вопрос. Например, сейчас список статей организован через метод fetch, который подтягивает json фал, что находится непосредственно в папке с сайтом, а посему доступ к нему запросто открывается через отладчик:
Понятно, когда вопрос коснётся конфиденциальности, такой подход неприемлем.
Тогда можно предположить, что пусть фалы json хранятся где-то на стороне, и доступ к ним ограничен политикой CORS. Однако снова хватает того же fetch, зачем nodejs? Только ли для того что с ним упрощается разработка?
И, второй вопрос вдогонку. Я понимаю раз технология nodejs так популярна я довольно скоро получу ответ, почему стоит его использовать, но в инете я пока нахожу один и тот же паттерн, вроде :»Ставим express и раздаём файлы в виде *some_content.js , а дальше уже недалеко до React или Vue» Но, nodejs это как по мне прежде всего серверный фреймворк, почему не использовать его в качестве помощника запрос-ответ, а рендер делать по старинке, или почти, типа статически динамических html? И, где его тогда устанавливать, во внутренней файловой структуре или обращаться к нему извне?
Варианты:
а) всё в одной папке;
б)клиент отдельно, nodejs и БД вместе, отдельно от клиента;
в) клиент и nodejs вместе, БД отдельно, nodejs только сервит но не рендерит на клиента;
г) клиент отдельно, nodejs отдельно, БД отдельно.
На сайте используются Web Components, в общем всё вручную, больше для полного понимания что где и почему.
Благодарю заранее.
P.S. Больше всего я застрял на атрибуте action тега form
Если я прописываю путь к обработчику в nodejs, то пока не нашёл как обработчик может дать команду рендерить страницу, которая находится на клиентской стороне. Везде описывается как получить соответствующую страницу с сервера. А у меня все шаблоны на клиенте, нужны только данные. Так, значит лучше использовать onsubmit preventDefault и обрабатывать запрос fetch в javascript файле? Где-то здесь пробел большой.- Вопрос задан более года назад
- 277 просмотров
Комментировать
Решения вопроса 1
sajtim @sajtim Автор вопросаОказалось, тема не так очевидна, и типового решения нет, как для первого так и для второго вопросов. Вариантов много, интернет на месте не стоит, поэтому вопрос решаем в индивидуальном порядке.
Кто, вдруг, зашёл в поисках решения, могу предложить искать безопасность сайта клиент-сервер по первому вопросу, файловая структура nodejs по второму.
Прошу не винить за холиварную тему.
Благодарю всех за ответы и комментарии.Ответ написан более года назад
Комментировать
Нравится Комментировать
Ответы на вопрос 2
Программистnodejs это бекенд то есть веб сервер
fetch выполняет ajax запросы к серверу ему пофиг нода там или нет
эти вещи никак не связанычто касается второго вопроса
клиент , нода как вебсервер и БД опять же разные вещи выполняющие разные функции понятие вместе или отдельно не имеет смыслаОтвет написан более года назад
Нравится 1 2 комментария
sajtim @sajtim Автор вопроса
Fetch не может выполнять запросы напрямую к БД?sajtim, напрямую к БД — нет, тк у баз данных используются свои протоколы, а не http.
Ну и не будете же вы давать полный доступ к бд всем пользователям ?)true @RAFAILgaley
лучше начать с пхп
и если его не хватит то использовать nodejs
nodejs нужен например для сервера который выполняя кучу запросов клиентов еще паралельно делает сложные вычисления например для многопользовательской игрыначни с простого сервера на котором простое api которое записывает в файлы читает и отдает json
без ничего, без фреймворков и готовых субд
просто шобы понять клиент-серверную архитектуру и как работать с даннымии чтобы защитить файлы на сервере настрой файл htaccess
расскажу подробнее, спрашивай
Ответ написан более года назад
Нравится 1 8 комментариев
sajtim @sajtim Автор вопросаПростите, искренне признателен, но здесь отметил «для себя давно решил, что это будет только javascript с его экосистемой»
true @RAFAILgaley
sajtim, дык пожалуйста ради бога
на любом типе сервера начни с простого json-api на файлах без готовой субд и без фреймворков
sajtim @sajtim Автор вопроса
!?, Я это как раз и пробую. Завис конкретно на form action (в P.S. вопроса)
Что делает action? Я уже отправляю запрос POST, в action, но пока он некорректно обрабатывается nodejs.
Я пока не хочу обрабатывать через onsubmit, главное понять, что делается при подходе с action. Как сервер может отправить сигнал загрузить другую страницу на клиенте?
Я отправляю форму в ноду из контактной формы. Пока пробую на локальном компе, если надо тест формы могу подгрузить. Как нода может сказать сайту перейти к примеру на index.html?
Или это делается только через onsubmit?true @RAFAILgaley
sajtim, формы вообще не нужны
это лишнее устаревшеевсё очень просто
вот что такое json-api —клиент посылает json на сервер
и сервер в ответ посылает json
и когда клиент его получает то делает то что ему надоболее сложный способ через сокеты:
клиент и сервер конектятся
и сервер посылает json-сообщения когда у него что то происходит
и клиент получив данные делает то что ему надоTheAndrey7,
Что мне мешает постучать на сервер по прямому ip?
А не через ссылку на сайт.
Тем самым моя папка будет с корня вашего сервера.Прощай, 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 документации:
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
Наш fetch запрос будет выглядеть так:
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-объект. Это означает, что в результате вызова метода json() мы получим Promise, т.к. чтение из подобного объекта является асинхронным.
Метаданные ответа
В предыдущем примере мы изучили, как можно проверить статус объекта ответа и конвентировать сам ответ в 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); >);
Типы ответа
Когда мы делаем 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» в этом объекте:
fetch('http://some-site.com/cors-enabled/some.json', ) .then(function(response) < return response.text(); >) .then(function(text) < console.log('Request successful', text); >) .catch(function(error) < log('Request failed', error) >);
Цепочки Promises
Одной из прекрасных особенностей Promise’ов является возможность группировать их в цепочки. Если говорить о них в скоупе fetch() , то они позволяют нам «шарить» логику между запросами.
Если вы работаете с JSON API, вам потребуется проверить статус и распарсить JSON для каждого ответа. Вы можете упростить свой код, определив парсинг статуса и JSON как раздельные функции, которые вернут Promise’ы. Вам останется подумать только об обработке самих данных и, разумеется, исключений.
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); >);
Мы определяем функцию, которая проверяет response.status и возвращает результат: Promise.resolve() или Promise.reject() . Это первый вызванный метод в нашей цепочке, и если он успешно завершается( Promise.resolve() ), то вызывается следующий за ним метод — fetch() , который, в свою очередь, опять возвращает Promise от response.json() . После этого вызова, в случае удачного выполнения, у нас будет готовый JSON объект. Если парсинг провалится, Promise будет отменен и сработает условие возникновения исключения.
Но самое лучшее здесь — это возможность переиспользовать такой код для всех fetch-запросов в приложении. Такой код проще поддерживать, читать и тестировать.
POST запрос
Уже давно никого не удивишь необходимостью использовать POST метод с передачей параметров в «теле» запроса для работы с API.
Чтобы осуществить такой запрос, мы должны указать соответствующие параметры в объекте настроек 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); >);
Посылаем учётные данные через Fetch-запрос
Если вы хотите отправить запрос с каким-либо учётными данными (например, с cookie), вам следует установить `credentials` в опциях запроса на «include»:
fetch(url, < credentials: 'include' >)
FAQ
Могу ли я отменить fetch() запрос?
В настоящий момент это невозможно, но это активно обсуждается на GitHubСуществует ли полифил?
ДаПочему «no-cors» реализован для service workers, но не для window?
Это было сделано из соображений безопасности. Подробнее можно ознакомиться здесь.- Веб-разработка
- JavaScript
- Программирование