Журнал • знания • практикаПоиск по сайту

Сайты, дизайн и самостоятельная работа

Найти материал →

Обработка ошибок при отправке данных через fetch() в веб-разработке

6 минут чтения

Обработка ошибок при отправке данных через fetch() в веб-разработке

Чтобы обработать сбой при отправке данных через fetch(), отдельно проверяйте сетевую ошибку, HTTP-статус и разбор тела ответа. Учитывайте пустые ответы, ограничивайте время ожидания и повторяйте запрос только тогда, когда это безопасно. Такой порядок делает обработку ошибок fetch JavaScript предсказуемой и помогает показать пользователю понятное сообщение.

Опорные решения для надёжного fetch()

  • Проверяйте response.ok или response.status: HTTP-ошибка сама по себе не отклоняет промис fetch().
  • Различайте сетевой сбой, ответ с ошибочным HTTP-статусом и ошибку разбора ответа.
  • Перед чтением JSON учитывайте пустое тело и неверный формат данных.
  • Прерывайте запрос по тайм-ауту или действию пользователя через AbortController.
  • Не повторяйте автоматически запросы, которые могут создать дублирующую операцию.
  • Показывайте краткое сообщение пользователю, а технические детали сохраняйте отдельно.

Проверьте статус ответа до обработки данных

Эта проверка подходит для запросов, которые должны вернуть успешный статус и данные: например, при отправке формы или сохранении настроек. Она особенно важна, если клиент ожидает JSON, а сервер может вернуть ошибку с другим телом или вообще без тела.

Не нужно считать любой ответ с HTTP-статусом вне успешного диапазона сетевым сбоем: сервер доступен и вернул ответ, но операция не завершилась успешно. Если обработчик намеренно использует, например, статус для штатного сценария, предусмотрите его отдельно.

При отправке JSON задайте метод, заголовок и тело запроса. Не устанавливайте вручную заголовок Content-Type для FormData: браузер должен добавить его вместе с границей частей тела.

const response = await fetch("/api/profile", {
  method: "POST",
  headers: {
    "Content-Type": "application/json"
  },
  body: JSON.stringify({ name: "Анна" })
});

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

const data = await response.json();

Такой пример показывает, как отправить данные через fetch JavaScript. Для более полной fetch API обработки ошибок HTTP проверяйте статус до разбора тела и не полагайтесь только на блок catch.

Разведите сетевые сбои, HTTP-ошибки и ошибки разбора

Для базовой обработки понадобятся браузер с поддержкой fetch(), доступ к адресу API и понимание формата запроса и ответа. Полезны инструменты разработчика: вкладка сети помогает проверить отправленные заголовки, статус и тело ответа.

  • Сетевой сбой: запрос не получил доступный ответ. Причиной может быть потеря соединения, блокировка запроса или ошибка адреса.
  • HTTP-ошибка: ответ получен, но его статус указывает на неуспешный результат. Проверяйте response.ok.
  • Ошибка разбора: ответ получен, но его тело не соответствует ожидаемому формату, например JSON повреждён или тело пустое.

Поместите запрос, проверку статуса и чтение тела в один обработчик ошибок, но сохраняйте тип сбоя. Так вызывающий код сможет отличить отсутствие соединения от ответа сервера.

async function sendJson(url, payload, signal) {
  let response;

  try {
    response = await fetch(url, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(payload),
      signal
    });
  } catch (error) {
    if (error.name === "AbortError") {
      throw error;
    }
    throw new Error("Не удалось связаться с сервером", { cause: error });
  }

  const text = await response.text();

  if (!response.ok) {
    throw new Error(`Сервер вернул HTTP ${response.status}`);
  }

  if (!text) {
    return null;
  }

  try {
    return JSON.parse(text);
  } catch (error) {
    throw new Error("Сервер вернул некорректный JSON", { cause: error });
  }
}

Обработайте пустое или некорректное тело ответа

  1. Определите ожидаемый формат.
    Уточните в контракте API, возвращает ли операция JSON, текст или пустое тело. Не вызывайте response.json() автоматически, если сервер может ответить без содержимого.
  2. Проверьте статус до разбора.
    Если ответ неуспешный, сначала зафиксируйте статус, а затем при необходимости прочитайте тело ошибки. Тело ответа можно прочитать только один раз.
  3. Обработайте пустой ответ.
    При чтении через response.text() проверьте пустую строку и верните подходящее значение, например null. Для такого сценария не вызывайте JSON.parse().
  4. Перехватите ошибку формата.
    Оберните JSON.parse() или response.json() в try...catch. Сообщите, что ответ сервера не удалось обработать, и сохраните исходную ошибку для диагностики.
  5. Сверьте поведение с API.
    Проверьте фактический статус и тело в инструментах разработчика. Если сервер возвращает текст или пустое тело по контракту, не считайте это ошибкой формата JSON.

Быстрый режим

  1. Отправьте запрос с нужным методом, заголовками и сериализованным телом.
  2. Перехватите сетевой сбой и отдельно обработайте отмену.
  3. Проверьте HTTP-статус до разбора ответа.
  4. Учтите пустое тело и перехватите ошибку JSON.

Ограничьте ожидание запроса и корректно отменяйте его

Перед выпуском проверьте обработку запроса по этому списку:

  • Для запроса задан допустимый срок ожидания или есть возможность отменить его вручную.
  • При тайм-ауте используется AbortController или поддерживаемый в целевых браузерах механизм AbortSignal.timeout().
  • Отмена пользователем не отображается как обычная ошибка сервера.
  • После отмены интерфейс возвращается в рабочее состояние.
  • Состояние загрузки сбрасывается и при успехе, и при ошибке.
  • Повторное нажатие не запускает конкурирующие запросы, если это нежелательно.

Пример с контроллером и таймером:

const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), 8000);

try {
  const response = await fetch("/api/save", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ enabled: true }),
    signal: controller.signal
  });

  if (!response.ok) {
    throw new Error(`HTTP ${response.status}`);
  }
} catch (error) {
  if (error.name === "AbortError") {
    console.log("Запрос отменён или превышено время ожидания");
  } else {
    console.error("Ошибка запроса", error);
  }
} finally {
  clearTimeout(timer);
}

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

Настройте повторы только для подходящих запросов

Автоматические повторы могут быть полезны при временных сбоях, но опасны для операций, которые изменяют данные. Проверьте, что повтор не создаст несколько записей и не приведёт к повторному списанию или отправке.

  • Не повторяйте запрос без разбора при любой ошибке из catch.
  • Не повторяйте автоматически операцию изменения данных, если сервер не поддерживает защиту от дубликатов.
  • Не повторяйте запрос после отмены пользователем.
  • Не повторяйте запрос с некорректным телом или неверными параметрами: сначала исправьте данные.
  • Не игнорируйте HTTP-статус и ответ сервера при выборе стратегии повтора.
  • Не запускайте повторы без ограничения количества попыток и паузы между ними.
  • Не оставляйте пользователя без сообщения, если запрос продолжает выполняться или повторы завершились неудачно.

Для безопасного повтора сначала убедитесь, что операция допускает повторное выполнение. Если API предоставляет ключ идемпотентности, используйте его согласно документации сервера.

Покажите понятную ошибку и сохраните детали для диагностики

  • Короткое сообщение в интерфейсе. Подходит для обычных форм: сообщите, что данные не удалось отправить, и предложите проверить соединение или повторить действие позже.
  • Ошибки валидации рядом с полями. Уместно, когда сервер возвращает сведения о некорректных значениях. Покажите пользователю конкретные поля, не заменяя серверную проверку клиентской.
  • Техническая запись для диагностики. Сохраняйте тип ошибки, HTTP-статус и контекст операции. Не записывайте в журнал пароли, токены и другие чувствительные данные.
  • Отдельная обработка отмены. Если пользователь сам прервал запрос, обычно достаточно вернуть интерфейс в исходное состояние без тревожного сообщения.

При обработке ошибок при отправке формы JavaScript разделите сообщение для пользователя и сведения для разработчика. Не показывайте необработанный ответ сервера: он может содержать внутренние детали реализации. Для дальнейшей практики подойдёт обучение JavaScript веб-разработка онлайн, но конкретное поведение запроса следует сверять с контрактом вашего API.

Нюансы обработки сбоев fetch() на практике

Отклоняет ли fetch() промис при ответе со статусом ошибки?

Обычно нет: ответ с HTTP-статусом ошибки всё равно приводит к выполнению промиса. Проверяйте response.ok или response.status самостоятельно.

Как отличить сетевой сбой от HTTP-ошибки?

Сетевой сбой обычно попадает в catch, потому что доступный HTTP-ответ не получен. HTTP-ошибку определяйте по статусу полученного объекта Response.

Что делать, если сервер вернул пустое тело?

Сверьте поведение с контрактом API. Если пустой ответ допустим, обработайте его отдельно и не пытайтесь разбирать пустую строку как JSON.

Можно ли читать тело ответа в нескольких местах?

Нет, тело ответа обычно можно прочитать только один раз. Считайте его заранее, например через response.text(), а затем используйте полученную строку для проверки и разбора.

Как обрабатывать тайм-аут fetch()?

Используйте AbortController или поддерживаемый в целевой среде механизм тайм-аута. В обработчике отдельно распознавайте отмену, чтобы не смешивать её с сетевым сбоем.

Когда безопасно повторить POST-запрос?

Только если повтор не создаёт нежелательных дубликатов или API предоставляет подходящий механизм защиты от них. Учитывайте статус ответа и условия повтора, описанные сервером.

Scroll to Top