Ajax — Примеры

Пример 1 — Завершившийся запрос, принятый за успех

Кнопка перезагружает суммы в корзине, и стоит серверу вернуть статус ошибки, как панель заполняется сбивающей с толку ошибкой разбора; пока же запрос в полёте, не показывается ничего. Объясните обе неисправности, а затем смоделируйте три исхода. Путь эндпоинта ниже — заглушка, подставьте свой.

async function refreshTotals() {
  const response = await fetch("/api/cart/totals");
  const totals = await response.json();          // тело 500 разбирается как полезная нагрузка
  document.querySelector("[data-total]").textContent = formatMoney(totals.total);
}

Решение

async function refreshTotals() {
  const panel = document.querySelector("[data-total]");
  panel.textContent = "Загрузка…";

  try {
    const response = await fetch("/api/cart/totals");
    if (!response.ok) throw new Error(`HTTP ${response.status}`);

    const totals = await response.json();
    if (typeof totals?.total !== "number") throw new Error("unexpected payload");
    panel.textContent = formatMoney(totals.total);
  } catch (error) {
    panel.textContent = "Не удалось загрузить суммы";
  }
}

Успешно завершённым промисом приходит не только удачный ответ: fetch отклоняется лишь при сетевом сбое, поэтому 404 или 500 — тоже успех, и тело страницы с ошибкой уходит прямо в парсер JSON. Вот почему сбой всплывает как ошибка разбора и называет не ту проблему, а проверка response.ok превращает его в статус, с которым уже можно что-то сделать. Состояние — вторая неисправность: там, где нет ни ветки загрузки, ни ветки ошибки, панель сохраняет прежнее содержимое и выглядит сломанной, а не занятой. Исправление здесь исчерпывается тремя явными исходами — загрузка, готово, ошибка, — а проверка формы разобранного значения ловит тот случай, когда успешный ответ всё равно не является ожидаемой полезной нагрузкой.

Пример 2 — Запись, которую сервер получает как пустое тело

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

async function saveAddress(address) {
  const response = await fetch("/api/addresses", {
    method: "POST",
    body: JSON.stringify(address),
  });
  return response.json();
}

Решение

async function saveAddress(address) {
  const response = await fetch("/api/addresses", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(address),
  });

  if (!response.ok) throw new Error(`HTTP ${response.status}`);
  return response.json();
}

Раскодировать байты серверу нечем, если тип содержимого не указан, поэтому сериализованный объект приходит как неразбираемое или пустое тело. Сам запрос при этом завершился успешно, и сбой выглядит как ошибка на сервере, а не на клиенте. Тело становится интерпретируемым именно благодаря Content-Type: application/json, и сочетается он с JSON.stringify, а не с полезной нагрузкой формы: форма должна отправляться через FormData и без заголовка, чтобы браузер сам задал границу multipart.