Какой HTTP код ошибки вернуть при некорректных данных на входе
Что проверяют
Различаете ли вы синтаксическую и смысловую ошибку клиента и умеете ли выразить это в Go-хендлере. Следом часто спрашивают, в каком формате отдать тело ошибки и как не пропустить мусор при декодировании JSON.
Как отвечать
Короткий ответ: 400 Bad Request. Хороший ответ разводит случаи:
- 400 (
http.StatusBadRequest): запрос нельзя разобрать. Битый JSON, строка вместо числа, неизвестные поля, неверный query-параметр; - 422 (
http.StatusUnprocessableEntity, в RFC 9110 он называется Unprocessable Content): JSON корректен, но значения нарушают правила. Дата окончания раньше начала, отрицательное количество; - 415 (
http.StatusUnsupportedMediaType): пришёл не тотContent-Type; - 413 (
http.StatusRequestEntityTooLarge): тело больше допустимого.
Многие команды отдают 400 на все ошибки валидации, и это нормально, если правило одно на весь API и записано в документации. Важнее тело: машинно-читаемый код ошибки и список полей. Для этого есть стандарт Problem Details, RFC 9457, с типом application/problem+json.
В Go порядок такой: ограничить тело через http.MaxBytesReader, создать json.NewDecoder(r.Body) и вызвать DisallowUnknownFields(), декодировать в структуру, а не в map[string]any. Ошибку декодирования отдать как 400, после неё запустить валидацию (руками или через go-playground/validator) и вернуть 422 или 400 по принятому правилу. Заголовок Content-Type выставляется до WriteHeader, иначе в ответ он не попадёт.
Где спотыкаются
Возвращают 500 на любую ошибку разбора. Клиент думает, что упал сервер, начинает повторять запрос, а мониторинг показывает ложные инциденты.
Отдают 404, когда в теле указан id несуществующей связанной сущности. Ресурс из URL при этом существует, и клиент путается: 404 уместен для пути, для поля в теле честнее 422 или 400.
Используют http.Error в JSON API. Он ставит Content-Type: text/plain, и клиент, который ждёт JSON, не может разобрать ошибку.