Если ты читаешь эту статью, значит, ты уже начал погружаться в асинхронный 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 из примера выше в статье, так как перед парсингом, проверяем есть ли у нас успешный ответ от сервера, что бы достать из него данные.
Итак, правильное мышление
-
Запрос достиг сервера?
Если да, то это успешная отправка. Если нет — ошибка попадет вcatch
. -
Что ответил сервер?
Даже если запрос дошел до сервера, ответ может быть ошибочным. Это нужно проверять вручную с помощьюresponse.ok
. -
Не надеяться на
catch
для обработки всех ошибок.
catch
отловит только ошибки, связанные с отправкой запроса. Ошибки сервера нужно обрабатывать отдельно.
Заключение
Проверка response.ok
— это важная часть работы с fetch
. Она помогает убедиться, что запрос выполнился успешно, и избежать ошибок, связанных с некорректными данными. Не забывай обрабатывать разные статусы ответов и предоставлять понятные сообщения об ошибках. Это сделает твой код более надежным и удобным для отладки.
Проверка response.ok
— это только первый шаг. В реальных приложениях важно обрабатывать разные статусы ответов и предоставлять пользователю или разработчику понятные сообщения об ошибках.
Автор: Danil_Putro