Skip to content

Latest commit

 

History

History
348 lines (229 loc) · 19.4 KB

11. Работа с Рест АПИ через fetch, а также про ХТТП от и до.md

File metadata and controls

348 lines (229 loc) · 19.4 KB

Работа с Рест АПИ через fetch, а также про ХТТП от и до

Как брать данные, где брать данные и что такое асинхронное программирование

Про ХТТП

iframe: https://www.youtube.com/embed/WzjDqrS22Jg

Если вы читали второй урок курса по вёрстке, то вы помните, что весь интернет работает на ХТТП, связке "клиенты-сервер" и, конечно, запросах с ответами.

ХТТП

ХТТП это протокол передачи данных: мы отправляем к серверу запрос и получаем ответ.

Мы — это клиент, а клиентом может быть хоть wget или curl из Терминала, хоть браузер. Хоть микроволновка на Андроиде.

Как выглядит запрос? У него есть адрес и метод: GET, POST, PUT, DELETE, PATCH, OPTIONS — вы делаете запрос на адрес и указываете метод.

Зачем это нужно? У каждого ресурса может быть поддержка нескольких методов: например, на POST /users создаётся новый пользователь, а на GET — запрашивается список.

  • GET — запросить данные,
  • POST — создать новую сущность,
  • PUT — обновить существующую,
  • DELETE — удалить её,
  • PATCH — частично обновить,
  • OPTIONS — доступные опции у ресурса

Примеры?

  • GET /users — запросить список пользователей,
  • POST /users — создать пользователя,
  • PUT /users/evgenyrodionov — обновить пользователя evgenyrodionov,
  • PATCH /users/evgenyrodionov — частично обновить пользователя evgenyrodionov,
  • OPTIONS /users — запросить опции, доступные на ресурсе (допустим, параметры фильтрации).

Есть много вопросов, давайте на них отвечу. Начнём снизу.

Как передавать параметры

Для этого есть кверипарамс (query params) — это те параметры, которые указываются у адреса, например, GET /users?state=active&offset=20&courseId=react

Кверипарамс это всего лишь строка в формате key=value и объединением через &.

В джсе легче всего работать через пакет qs — он преобразовывает кверипарамс в объект и обратно.

В чём отличие PATCH от PUT

В PUT уходит новый объект, а в PATCH — список изменений.

А как это передавать

Для этого есть тело запроса, обычно это Джаваскрипт объект (или JSON).

POST /users

{
  "username": "evgenyrodionov",
  "email": "[email protected]",
  "age": 24
}

Для Джсона используется контент-тайп application/json, но бывают ещё multipart/form-data (раньше для форм, сейчас в основном для файлов). Что это такое? Это майм-типы.

А как их указывать

Для этого используется заголовок content-type: application/json.

Заголовок?

Да, у каждого запроса есть заголовки — это служебная информация для сервера, которую не стоит пихать в тело запроса.

Вот основные:

  • content-type — тип тела,
  • accept — в чём обрабатывается ответ (тоже майм-тип),
  • cookieкуки,
  • authorization — авторизационные данные (например, логин и зашифрованный пароль)

Обычно разработчик работает с этими заголовками.

А есть заголовки ответа?

Да.

Например?

Один из самых основных это status.

Статус это код ответа, по которому можно определить насколько успешно прошёл запрос. Кодов много, но вот основные:

2xx

Это успешные коды ответа.

  • 200 OK — сервер обработал запрос,
  • 201 Created — создан новый ресурс,
  • 204 No Content — сервер обработал запрос, но ему нечего прислать в теле.

3xx

Отвечают за редиректы, нужен заголовок location с новым адресом.

  • 301 Moved Permanently — ресурс навсегда переехал,
  • 302 Found — ресурс временно находится по другому адресу.

4xx

Запрос был отправлен с ошибкой на стороне клиента (например, неправильно поле заполнено).

5xx

Проблемы на стороне сервера.


Суммируем.

Веб работает на протоколе ХТТП по системе "клиент-сервер": клиент посылает запрос на адрес, сервер присылает ответ.

Клиент может передать заголовки и тело, а сервер может прислать заголовки и тело.

Клиент это любой инструмент, который работает с ХТТП — хоть другой сервер, хоть утилита в Терминале, хоть бот в Телеграме.

Но как со всем этим работать во фронтэнде?

Рест АПИ

Вы часто, наверное, слышите про Рест АПИ — мол, наш АПИ он РЕСТФул, мол, полностью по стандартам.

Ирония в том, что любой АПИ, построенный на ХТТП, будет рестовым — в этом и смысл ХТТП. Посмотрите на это определение из Википедии:

REpresentational State Transfer (REST), or RESTful, web services provide interoperability between computer systems on the Internet

В мире нет единого стандарта построения АПИ бэкэнда — каждый дрочит, как он хочет. Нет, серьёзно, посмотрите на эту тьму гайдлайнов: HackerNoon, studioarmix, Некий Филип, Майкрософт, Гугл, RestCase. Тысячи их!

Мне больше всего нравятся архитектура и документация у Страйпа — это эквайринг для приёма платежей. Почитайте как-нибудь.

Асинхронность, лайфсайклы и фетч

iframe: https://www.youtube.com/embed/ATYQtAjOgHE

Фетч

Раньше был XMLHttpRequest — браузерный АПИ для запросов. Неудобный аж жуть.

Поэтому когда появился Джквери с его функцией $.ajax(), люди выдохнули: наконец-то удобный способ для работы с ХТТП.

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

Затем этот подход забрали себе библиотеки axios и superagent.

Ну а позже появился новый браузерный АПИ fetch — сейчас его многие и используют. Если нужна поддержка старых браузеров — берут полифилл github/fetch от Гитхаба.

Собственно, Фетч. Фетч это АПИ браузера, которое работает с ХТТП: вы делаете запрос на адрес и обрабатываете ответ.

Чисто технически, это просто одна функция fetch(), которая построена на промисах. То есть на чём? Об этом позже.

В Фетч вы передаёте адрес и опции: от заголовков до тела запроса, вот пример —

const data = {
  username: "evgenyrodionov",
  email: "[email protected]",
  age: 24
};

fetch("/api/v1/users", {
  method: "POST", // метод
  body: JSON.stringify(data), // тело
  // заголовки
  headers: {
    "Content-Type": "application/json"
  },
  // работа с куки
  credentials: "same-origin"
})
  .then(
    function(response) {
      // https://github.github.io/fetch/#Response
      console.log(response.status); //=> number 100–599
      console.log(response.statusText); //=> String
      console.log(response.headers); //=> Headers
      console.log(response.url); //=> String

      return response.json();
    },
    function(error) {
      error.message; //=> String
    }
  )
  .then(function(responseAsJson) {
    console.log(responseAsJson);
    //  {
    //    "id": 1, // новое поле, потому что пользователь успешно создан
    //    "username": "evgenyrodionov",
    //    "email": "[email protected]",
    //    "age": 24
    //  }
  });

Окей, запрос понятен, вроде даже ответ как-то обрабатываем в функции, которую передаём в .then. Кстати, а что за .then?

Асинхронное программирование

На этом моменте мы узнаем интересный аспект: бывают синхронное и асинхронное программирование. К плаванью отношения не имеют.

Синхронное программирование это когда интерпретатор код выполняет строчка за строчкой:

const x = 1;

console.log(x); // 1

В асинхронном программировании мы не знаем, когда выполнится наша функция: то ли сразу же, то ли через 10 мс, то ли через 50 секунд, то ли через 20 минут. И как быть? Как с этим работать?

Существуют три способа.

Коллбэки

Коллбэки (callbacks) это простая функция, которая вызывается, когда выполнится функция, которую вы вызвали.

Вы знали, что setState() асинхронный? Если вы попробуете обратиться к стейту на следующей строчке после вызова setState(), вы можете удивиться, когда увидите старые данные.

import React from "react";

class Counter extends React.Component {
  state = {
    counter: 0
  };

  handleClick = () => {
    this.setState({ counter: this.state.counter + 1 });
    console.log(this.state); // 0
  };

  render() {
    return (
      <React.Fragment>
        <pre>{JSON.stringify(this.state, null, 2)}</pre>
        <button onClick={this.handleClick}>+</button>
      </React.Fragment>
    );
  }
}

iframe: https://codesandbox.io/embed/0mqo1ol5yl

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

Почему setState асинхронный? Потому что бывает, что Реакт накапливает вызовы и потом разом обновляет стейт: так производительнее.

Как с этим работать? Обращайтесь к стейту в коллбеке.

this.setState({ counter: this.state.counter + 1 }, function() {
  console.log(this.state); // 1
});

Коллбек это функция, которая передаётся аргументом и вызывается, когда код отработан. Пример:

function multiplyNumber(number, cb) {
  const multiplied = number * number;

  cb(multiplied);
}

console.log(multiplyNumber(2)); // undefined, потому что нет return в функции

multiplyNumber(2, function(result) {
  console.log(result); // 4
});
Промисы

Промисы (Promises) это обещания. Чисто технически, это те же коллбеки, но на стероидах: вместо аргумента мы используем методы .then() и .catch() у функции-промиса. Фетч работает на промисах. В методы мы передаём функцию.

fetch(url, params)
  .then(function(response) {
    console.log(response);
  })
  .catch(function(error) {
    console.log(error);
  });
Асинк-эвейт

В 2017 году в Экмаскрипт (стандарт, на котором работает Джаваскрипт) привезли новую фичу: async-await — способ писать в синхронном стиле асинхронный код.

Асинк-эвейты работают вместо промисов в том числе.

const response = await fetch(url, params);

console.log(response);

А как работать с ошибками? Через конструкцию try..catch:

try {
  const response = await fetch(url, params);

  console.log(response);
} catch (error) {
  console.log(error);
}

Всегда обрабатывайте ошибки! Даже если знаете, что придут "чистые" данные: я так понадеялся на Телеграм и однажды свалил сайт на полчаса, потому что Телеграм стал присылать ошибку, которую я не обрабатывал — из-за этого падал весь бэкэнд.

Когда он перезапускался, то заново пытался выполнить и терпел неудачу, снова падая. С тех пор у меня весь код в try..catch.


Резюмируя: для ХТТП-запросов мы используем Фетч, который работает асинхронно.

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

Для асинхронного программирования есть три подхода в Джс: коллбэки, промисы и асинк-эвейт. Чтобы не было проблем и не бегать с горящей жопой, всегда нужно ловить ошибки и их обрабатывать: либо для себя выводить, либо использовать сервис тип Сентри или Багснега, либо показать ошибку пользователю.

Кстати, асинхронное программирование нужно, чтобы не блокировать основной поток исполнения кода: представьте, как бы вас бесил интерфейс, если бы он замерзал (даже скроллить нельзя!) пока не получил ответ или не обновил стейт. Ужасно же.

Итог

Сегодня мы прошлись по устройству ХТТП, узнали про асинхронное программирование, коллбэки, промисы и асинк-эвейты и Фетч, который работает на последних двух подходах.

И помните: всегда обрабатывайте ошибки. Это чертовски важно, хоть и рутинно сложно. Лучше пользователь увидит непонятное "произошла ошибка", чем не поймёт что произошло. А ещё лучше, если он узнает на чьей она стороне и что он может с этим сделать.