Как фронтенд должен обрабатывать отсутствие ответа на POST запрос к API
Что проверяют
Понимаете ли вы, что отсутствие ответа не означает, что запрос не выполнился. Для Go-разработчика вопрос почти всегда сводится к бэкенду: как сделать POST безопасным для повтора. Следом спрашивают, как вы реализуете идемпотентность в своём сервисе.
Как отвечать
Начните с клиента: у запроса есть таймаут (AbortSignal.timeout() в браузере), после него пользователь видит понятный статус, введённые данные не пропадают, повтор идёт с экспоненциальной задержкой и джиттером. Но слепой повтор POST опасен: первый запрос мог дойти, и заказ создастся дважды.
Поэтому основная часть ответа про сервер:
- ключ идемпотентности: клиент генерирует UUID на одно действие пользователя и шлёт его в заголовке
Idempotency-Key. Сервер сохраняет ключ с уникальным ограничением в той же транзакции, что и бизнес-запись, а на повтор возвращает сохранённый ответ; - конкурентные повторы: если второй запрос пришёл, пока первый ещё выполняется, отвечайте
409 Conflictили ждите завершения, но не выполняйте операцию второй раз; - таймауты сервера: у
http.ServerзадайтеReadHeaderTimeout,ReadTimeout,WriteTimeout,IdleTimeout. По умолчанию они нулевые, то есть ограничения нет; - контекст: при обрыве соединения
r.Context()отменяется. Решите, где операцию можно прервать, а где её надо довести до конца независимо от клиента, для этого естьcontext.WithoutCancel(с Go 1.21); - исходящие вызовы: у
http.DefaultClientтаймаута нет, для вызовов других API нужен свойhttp.ClientсTimeoutили контекст с дедлайном.
Для долгих операций лучше сразу отвечать 202 Accepted с идентификатором задачи, а статус клиент узнаёт отдельным GET.
Где спотыкаются
Предлагают просто повторить запрос по таймауту. Без ключа идемпотентности это дубли платежей и заказов.
Смешивают ответ с ошибкой и отсутствие ответа. На 4xx повторять бессмысленно (кроме 408 и 429), на 5xx и сетевую ошибку можно, но только для идемпотентной операции.
Проверяют ключ отдельным SELECT перед вставкой. Два параллельных запроса оба не находят ключ и оба выполняются: гонку закрывает только уникальный индекс или блокировка.