Почему важно проверять response.ok в Fetch API и почему HTTP-ошибки не вызывают отклонение промисов

в 8:16, , рубрики: fetch, http, javascript, response

Если ты читаешь эту статью, значит, ты уже начал погружаться в асинхронный JavaScript и, в частности, в работу с промисами и fetch. Как и в любом новом деле, здесь есть свои нюансы, которые важно понимать, чтобы писать надежный код. Один из таких нюансов — это проверка на response.ok. Давай разберемся, зачем это нужно и почему без этого можно попасть в неприятности.

Посмотри, пожалуйста, на эту строчку кода:

fetch(url).then(response => response.json()).catch(error => console.log(error))

Если ты полагаешь, что обезопасил себя с помощью catch и узнаешь обо всех ошибках, то стоит дочитать эту статью до конца и разобраться детально.


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

В чем разница между успешной отправкой запроса и результатом запроса?

Успешная отправка запроса - это когда запрос достиг сервера. Это технический этап, который зависит от твоего кода и сети. Если запрос отправлен, и сервер его получил, то это уже успех с точки зрения fetch. А если на этом этапе происходит ошибка, то fetch выбрасывает исключение которое проваливается в catch.

Что может пойти не так на этапе отправки запроса:

  • Сервер недоступен (например, нет интернета)

  • DNS-запрос не удался (например, неверный домен)

  • Ошибка CORS (Cross-Origin Resource Sharing), если сервер не разрешает запросы с текущего источника

  • Если запрос был отменён с помощью AbortController

  • Некорректный JSON (сервер вернул ответ, который не является корректным JSON)

fetch(url) // Указан неверный домен, связь с сервером не установлена
  .then((response) => response.json())
  .catch((error) => {
    console.log(error); // TypeError: Failed to fetch
  });

Здесь проблема со стороны fetch, он берёт за это ответственность и сообщает тебе об этом сам, через catch

fetch(url) 
  .then((response) => {
    // Запрос отправлен успешно и мы пытаемся сделать парсинг json из ответа 
    // сервера
    // Но сервер ответил 404 Not Found и нам нечего парсить
    return response.json()
  })
  .catch((error) => {
    console.log(error); // SyntaxError: Failed to execute 'json' on 'Response'
  });

В данном коде исключение возникает из-за попытки парсинга JSON из ответа сервера, который вернул статус 404 (Not Found). Всё потому, что когда вы вызываете response.json(), метод пытается парсить тело ответа как JSON. Если тело ответа не является валидным JSON, возникает исключение SyntaxError, которое автоматически проваливается в catch.

Мы можем это предотвратить предварительной проверкой на response.ok , о котором поговорим далее.

Что важно понять, это то, что fetch тебе говорит: "Бро, это мой косяк, я не смог отправить запрос на сервер и не смог сделать парсинг JSON и сейчас я расскажу тебе почему, смотри в catch"


Результат запроса — это уже ответ от сервера. Здесь важно понимать, что сервер может получить твой запрос, но вернуть ответ с ошибкой, со статусом 4xx, 5xx. Или успешный ответ с статусом 2xx.

В случаях с ошибками, это может быть, например:

  • Ты запросил несуществующую страницу, и сервер вернул 404 Not Found

  • У тебя нет прав доступа к ресурсу, и сервер вернул 403 Forbidden

  • На сервере произошла внутренняя ошибка, и он вернул 500 Internal Server Error

Но, внимание, эти ошибки не попадают в catch, потому что запрос технически был успешно доставлен на сервер, и сервер вернул ответ. Ответ просто указывает на то, что что-то пошло не так. Именно здесь и нужна проверка response.ok

Со стороны fetch это выглядит так: "Бро, я доставил запрос до сервера успешно, ко мне вопросов не может быть, дальше сам..."

fetch(url)
  .then((response) => {
    console.log(response.status) // Ответ от сервера 404 
  })
  .catch((error) => {
    console.log(error); // Ошибка 404 здесь не перехватывается
  });

Почему в этом примере код catch не отловит ошибки сервера?

Блок catch срабатывает только на исключения, которые возникают в процессе выполнения кода. Когда сервер возвращает ответ с кодом ошибки (например, 404 или 500), это не исключение — это просто ответ. Fetch считает, что запрос выполнен успешно, потому что он получил ответ от сервера. Поэтому, если ты не проверяешь response.ok, твой код продолжит выполняться, как будто всё в порядке, даже если сервер вернул ошибку.

Исправляем ситуацию:

fetch(url)
  .then(response => {
    if(!response.ok) {
      throw new Error(`Something went wrong: ${response.status}`)
    }
    return response.json()
  })
  .catch(error => {
    console.log(error)
  })

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

Итак, правильное мышление

  1. Запрос достиг сервера?
    Если да, то это успешная отправка. Если нет — ошибка попадет в catch.

  2. Что ответил сервер?
    Даже если запрос дошел до сервера, ответ может быть ошибочным. Это нужно проверять вручную с помощью response.ok.

  3. Не надеяться на catch для обработки всех ошибок.
    catch отловит только ошибки, связанные с отправкой запроса. Ошибки сервера нужно обрабатывать отдельно.

Заключение

Проверка response.ok — это важная часть работы с fetch. Она помогает убедиться, что запрос выполнился успешно, и избежать ошибок, связанных с некорректными данными. Не забывай обрабатывать разные статусы ответов и предоставлять понятные сообщения об ошибках. Это сделает твой код более надежным и удобным для отладки.

Проверка response.ok — это только первый шаг. В реальных приложениях важно обрабатывать разные статусы ответов и предоставлять пользователю или разработчику понятные сообщения об ошибках.

Автор: Danil_Putro

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js