Ошибка валидации запроса самп

Обновлено: 05.07.2024

Почти все разработчики так или иначе постоянно работают с api по http, клиентские разработчики работают с api backend своего сайта или приложения, а бэкендеры "дергают" бэкенды других сервисов, как внутренних, так и внешних. И мне кажется, одна из самых главных вещей в хорошем API это формат передачи ошибок. Ведь если это сделано плохо/неудобно, то разработчик, использующий это API, скорее всего не обработает ошибки, а клиенты будут пользоваться молчаливо ломающимся продуктом.

За 7 лет я как поддерживал множество legacy API, так и разрабатывал c нуля. И я поработал, наверное, с большинством стратегий по возвращению ошибок, но каждая из них создавала дискомфорт в той или иной мере. В последнее время я нащупал оптимальный вариант, о котором и хочу рассказать, но с начала расскажу о двух наиболее популярных вариантах.

Если у вас примитивная бизнес-логика или API из 5 url, то в принципе это нормальный подход. Однако как-только бизнес-логика станет сложнее, то начнется ряд проблем.

REST скорее концепция, чем формат общения из чего следует неоднозначность использования статусов. Разработчики используют статусы как им заблагорассудится. Например, некоторые API при отсутствии сущности возвращают 404 и текст ошибки, а некоторые 200 и пустое тело.

Бэкенд разработчику в проекте непросто выбрать статус для ошибки, а клиентскому разработчику неочевидно какой статус предназначен для того или иного типа ошибок бизнес-логики. По-хорошему в проекте придется держать enum для того, чтобы описать какие ошибки относятся к тому или иному статусу.

Когда бизнес-логика приложения усложняется, начинают делать как-то так:

Принудительный вызов валидации

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

Тем не менее, Spring Boot предоставляет предварительно сконфигурированный экземпляр валидатора. Внедрив этот экземпляр в сервис не придется создавать его вручную.

Когда этот сервис внедряется Spring, в конструктор автоматически вставляется экземпляр валидатора.

Группы валидаций

Некоторые объекты участвуют в разных вариантах использования.

Возьмем типичные операции CRUD: при обновлении и создании, скорее всего, будет использоваться один и тот же класс. Тем не менее, некоторые валидации должны срабатывать при различных обстоятельствах:

  • только перед созданием
  • только перед обновлением
  • или в обоих случаях

Функция Bean Validation, которая позволяет нам внедрять такие правила проверки, называется "Validation Groups".

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

Для нашего примера CRUD определим два маркерных интерфейса OnCreate и OnUpdate :

Затем используем эти интерфейсы с любой аннотацией ограничения:

Это позволит убедиться, что id пуст при создании и заполнен при обновлении.

Spring поддерживает группы проверки только с аннотацией @Validated

Обратите внимание, что аннотация @Validated применяется ко всему классу. Чтобы определить, какая группа проверки активна, она также применяется на уровне метода.

Использование групп проверки может легко стать анти-паттерном. При использовании групп валидации сущность должна знать правила валидации для всех случаев использования (групп), в которых она используется.

Сначала нужно определить эту структуру данных. Назовем ее ValidationErrorResponse и она содержит список объектов Violation :

Затем создадим глобальный ControllerAdvice , который обрабатывает все ConstraintViolationExventions , которые пробрасываются до уровня контроллера. Чтобы отлавливать ошибки валидации и для тел запросов, мы также будем работать с MethodArgumentNotValidExceptions :

Здесь информацию о нарушениях из исключений переводится в нашу структуру данных ValidationErrorResponse .

Обратите внимание на аннотацию @ControllerAdvice , которая делает методы обработки исключений глобально доступными для всех контроллеров в контексте приложения.

Валидация сущностей JPA

Persistence Layer это последняя линия проверки данных. По умолчанию Spring Data использует Hibernate, который поддерживает Bean Validation из коробки.

Обычно мы не хотим делать проверку так поздно, поскольку это означает, что бизнес-код работал с потенциально невалидными объектами, что может привести к непредвиденным ошибкам.

Допустим, необходимо хранить объекты нашего класса Input в базе данных. Сначала добавляем нужную JPA аннотацию @Entity , а так же поле id :

Когда репозиторий пытается сохранить невалидный Input , чьи аннотации ограничений нарушаются, выбрасывается ConstraintViolationException .

Bean Validation запускается Hibernate только после того как EntityManager вызовет flush.

Чтобы отключить Bean Validation в репозиториях Spring, достаточно установить свойство Spring Boot spring.jpa.properties.javax.persistence.validation.mode равным null .

№2: На все 200

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

На самом деле формат зависит от вас или от выбранной библиотеки для реализации коммуникации, например JSON-API.

В некоторых случаях, если есть библиотека десериализации данных, она может взять часть работы на себя. Писать SDK вокруг такого подхода проще нежели вокруг той или иной имплементации REST, ведь реализация зависит от того, как это видел автор. Кроме того, теперь никто не вызовет случайное срабатывание alert в мониторинге из-за того, что выбрал неудачный код ошибки.

Но неудобства тоже есть:

Избыточность полей при передаче данных, т.е. нужно всегда передавать 2 поля: для данных и для ошибки. Это усложняет чтение логов и написание документации.

При использовании средств отладки (Chrome DevTools) или других подобных инструментов вы не сможете быстро найти ошибочные запросы бизнес логики, придется обязательно заглянуть в тело ответа (ведь всегда 200)

нельзя делать повторы для неудавшихся GET запросов (на backend) на реверс-прокси (например, nginx) по указанной выше причине

имеются проблемы с документированием – swagger и ApiDoc не подходят, а удобных аналогов я не нашел

Итог: Для сложной бизнес-логики с большим количеством типов ошибок такой подход лучше, чем расплывчатый REST, не зря в проектах c “разухабистой” бизнес-логикой часто именно такой подход и используют.

№3: Смешанный

400 – ошибка бизнес логики

остальное ошибки в транспорте

Мы можем расширять объект ошибки для детализации проблемы, если хотим. С мониторингом все как во втором варианте, дописывать парсинг придется, но и риска “стрельбы” некорректными alert нету. Для документирования можем спокойно использовать Swagger и ApiDoc. При этом сохраняется удобство использования инструментов разработчика, таких как Chrome DevTools, Postman, Talend API.

Итог: Использую данный подход уже в нескольких проектах, где множество типов ошибок и все крайне довольны, как клиентские разработчики, так и бэкендеры. Внедрение новой ошибки не вызывает споров, проблем и противоречий. Данный подход объединяет преимущества первого и второго варианта, при этом код более читабельный и структурированный.

Самое главное какой бы формат ошибок вы бы не выбрали лучше обговорить его заранее и следовать ему. Если эту вещь пустить на “самотек”, то очень скоро обработка ошибок в проекте станет невыносимо сложной для всех.

P.S. Иногда ошибки любят передавать массивом

Но это актуально в основном в двух случаях:

Когда наш API выступает в роли сервиса без фронтенда (нет сайта/приложения). Например, сервис платежей.

Когда в API есть url для загрузки какого-нибудь длинного отчета в котором может быть ошибка в каждой строке/колонке. И тогда для пользователя удобнее, чтобы ошибки в приложении сразу показывались все, а не по одной.

Антонина Пшеничникова

SMMplanner

Антонина Пшеничникова

Антонина Пшеничникова

Когда захожу на вашу почту support. вобще происходит что-то непонятное, есть скрин

SMMplanner

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

Максим Захаров

SMM planner, здравствуйте. Сильно упали охваты после атвопостинга. Речь идёт о инстаграме. Возможно из-за сммпланера?

Добавьте возможность отмечать на видео для ленты в инстаграме. А то несерьезно как-то. В самой инсте уже около года это возможно.

SMMplanner

SMMplanner

Рома Егорихин

Произошла ошибка валидации.

Ни один сайт с автопостингом и тд не могу подключить.
Что я сделал до этого?
Создал бота по правилам , токен отправил на сайт вместе с ссылкой на канал (был новый канал для проверки и нужный мой канал) ни один не смог подключиться.
Пересоздал бота и добавил его в ManyBot (не знаю зачем) , все равно ошибка.

maksim92

Лепить такой код в каждый метод, у данного метода множество недостатоков, подойдет для супер мелких проектов.
Хоть и сделал пока рабочий вариант костыляво, хотелось бы что-то постабильнее.

maksim92

Andrew Stark, ну а если связать эти две вещи и вынести? Вы же программист симфони)

Правда это slim. Но на симфони можно сделать так, чтобы вообще этот метод убрать из контроллера.

Нередко пользователи пытаются передать в приложение некорректные данные. Это происходит либо из злого умысла, либо по ошибке. Поэтому стоит проверять данные на соответствие бизнес-требованиям.

Эту задачу решает Bean Validation. Он интегрирован со Spring и Spring Boot. Hibernate Validator считается эталонной реализацией Bean Validation.

Добавление пользовательского валидатора

Если имеющихся аннотаций ограничений недостаточно, то создайте новые.

В классе Input использовалось регулярное выражение для проверки того, что строка является IP адресом. Регулярное выражение не является полным: оно позволяет сокеты со значениями больше 255, таким образом "111.111.111.333" будет считаться действительным.

Давайте напишем валидатор, который реализует эту проверку на Java. Потому что как говорится, до решения проблемы регулярным выражением у вас была одна проблема, а теперь стало двe :smile:

Сначала создаем пользовательскую аннотацию @IpAddress :

Реализация валидатора выглядит следующим образом:

Теперь можно использовать аннотацию @IpAddress , как и любую другую аннотацию ограничения.

Валидация в Spring MVC Controller

  • тело запроса
  • переменные пути (например, id в /foos/)
  • параметры запроса

Рассмотрим каждый из них подробнее.

Валидация тела запроса

Тело запроса POST и PUT обычно содержит данные в формате JSON. Spring автоматически сопоставляет входящий JSON с объектом Java.

Проверяем соответствует ли входящий Java объект нашим требованиям.

  • Поле numberBetweenOneAndTen должно быть от 1 до 10, включительно.
  • Поле ipAddress должно содержать строку в формате IP-адреса.

Контроллер REST принимает объект Input и выполняет проверку:

Достаточно добавить в параметр input аннотацию @Valid , чтобы сообщить спрингу передать объект Валидатору, прежде чем делать с ним что-либо еще.

Если класс содержит поле с другим классом, который тоже необходимо проверить — это поле необходимо пометить аннотацией Valid.

Проверка переменных пути и параметров запроса

Проверка переменных пути и параметров запроса работает по-другому.

Не проверяются сложные Java-объекты, так как path-переменные и параметры запроса являются примитивными типами, такими как int , или их аналогами: Integer или String .

Вместо аннотации поля класса, как описано выше, добавляют аннотацию ограничения (в данном случае @Min ) непосредственно к параметру метода в контроллере Spring:

Обратите внимание, что необходимо добавить @Validated Spring в контроллер на уровне класса, чтобы сказать Spring проверять ограничения на параметрах метода.

В этом случае аннотация @Validated устанавливается на уровне класса, даже если она присутствует на методах.

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

Валидация в сервисном слое

Можно проверять данные на любых компонентах Spring. Для этого используется комбинация аннотаций @Validated и @Valid .

Аннотация @Validated устанавливается только на уровне класса, так что не ставьте ее на метод в данном случае.

Основы валидации Bean

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

При передаче размеченного таким образом объекта класса в валидатор, происходит проверка на ограничения.

Настройка

Добавьте следующие зависимости в проект:

Стандартные ограничения

Каждая аннотация имеет следующие поля:

Рассмотрим популярные ограничения.

@NotNull и @Null

@NotNull — аннотированный элемент не должен быть null. Принимает любой тип.
@Null — аннотированный элемент должен быть null. Принимает любой тип.

@NotBlank и @NotEmpty

@NotBlank — аннотированный элемент не должен быть null и должен содержать хотя бы один непробельный символ. Принимает CharSequence .
@NotEmpty — аннотированный элемент не должен быть null или пустым. Поддерживаемые типы:

  • CharSequence
  • Collection . Оценивается размер коллекции
  • Map . Оценивается размер мапы
  • Array . Оценивается длина массива

@NotBlank применяется только к строкам и проверяет, что строка не пуста и не состоит только из пробелов.

@NotNull применяется к CharSequence , Collection , Map или Array и проверяет, что объект не равен null . Но при этом он может быть пуст.

@NotEmpty применяется к CharSequence , Collection , Map или Array и проверяет, что он не null имеет размер больше 0.

Аннотация @Size(min=6) пропустит строку состоящую из 6 пробелов и/или символов переноса строки, а @NotBlank не пропустит.

Размер аннотированного элемента должен быть между указанными границами, включая сами границы. null элементы считаются валидными.

  • CharSequence . Оценивается длина последовательности символов
  • Collection . Оценивается размер коллекции
  • Map . Оценивается размер мапы
  • Array . Оценивается длина массива

Валидация конфигурации приложения

Spring Boot аннотация @ConfigurationProperties используется для связывания свойств из application.properties с Java объектом.

Данные из application необходимы для стабильной работы приложения. Bean Validation поможет обнаружить ошибку в этих данных при старте приложения.

Допустим имеется следующий конфигурационный класс:

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

Читайте также: