Выполнение HTTP-запросов в Node.js с помощью node-fetch
Веб-приложению часто требуется взаимодействовать с веб-серверами для получения различных ресурсов. Возможно, вам потребуется извлечь данные или отправить данные на внешний веб-сервер или API.
Используя клиентский JavaScript, этого можно достичь с помощью API fetch и функции window.fetch()
. В NodeJS несколько пакетов / библиотек могут достичь одного и того же результата. Один из них - node-fetch
.
node-fetch
- это небольшой модуль, который позволяет нам использовать функцию fetch()
в NodeJS, с функциональностью, очень похожей на нативный window.fetch()
, но с некоторыми отличиями:
A light-weight module that brings Fetch API to Node.js - node-fetch/node-fetch
Начало работы с 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
- содержат информацию о коде статуса HTTPok
- равно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, зарегистрированную в консоли:
<!doctype html>
<html itemscope="" itemtype="http://schema.org/WebPage" lang="en-RS">
<head>
<meta charset="UTF-8">
<meta content="origin" name="referrer">
<!-- Rest of the page -->
Получение данных 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 и выводили на консоль.
Запуск кода дает нам результат:
{
userId: 123,
title: 'loren impsum doloris',
completed: false,
id: 201
}
Обработка исключений и ошибок
Наши запросы иногда могут завершаться неудачно по разным причинам - из-за ошибки в функции 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: ${res.status} (${res.statusText})`)
}
}
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-запросами.