Почему не работает валидация над объектами типа DTO , а только над сущностями

299
05 сентября 2021, 20:20

У меня установлена аннотация над объектами типа dto, такая же как и над объектами типа Entity. Над сущностями аннотация срабатывает, а над объектами типа dto - нет.

Работаю в SrpingBoot.

application.properties

# ===============================
# NAME_TABLE
# ===============================
validate.packageid.size = "The field 'PACKAGEID' can contain only {max} symbols.";

Конфигурационный файл

@Configuration
public class ServiceConfig implements WebMvcConfigurer {

    @Bean
    public MessageSource messageSource() {
        ResourceBundleMessageSource source = new ResourceBundleMessageSource();
        source.setDefaultEncoding("UTF-8");
        source.setBasename("classpath:ValidationMessages");
        return source;
    }
    @Nullable
    @Override
    public Validator getValidator() {
        LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
        validator.setValidationMessageSource(messageSource());
        return validator;
    }
}

dto

@Size(message = "{validate.packageid.size}", max = 36)
    private String documentId;

entity

 @Column(name = "DOCUMENTID")
    @Size(message = "{validate.packageid.size}", max = 36)
    private String documentId;

Я не в контроллере их заполняю, я работаю с тестами и заполняю эти dto с помощью рефлексии.

Если бы это был контроллер, тогда аннотация @Valid указывала бы на то, что прежде чем передать разобранный json в dto, то нужно произвести валидацию.

У меня вот так наполняется dto

 /*подготовка объекта на сохранение*/
 private DTO prepareDto(String uuid){
    DTO dto = fillObjectData(new DTO ());
    dto.setPackageId(uuid);
    return dto;
}
 /**
     * Формируется объект с заполненными полями
     * @param source - объект, который нужно заполнить случайными значениями
     * @param <T> - тип целевого объекта который нужно вернуть с заполенными значениями
     */
    public static  <T> T fillObjectData(T source) {
        List<Object> values = new ArrayList<>();
        T dto;
        dto  = fillData(source, values);
        getData(dto, values);
        return dto;
    }
  /**
     *  Заполнение полей объекта.
     * @param object - обрабатываемый объект, любого типа данных
     * @param values - коллекция значений, которые будут сгенерированы для полей обрабатываемого объекта
     * @param <S> - универсальный параметр, указывает на тип данных передаваемого объекта
     * @return  - возвращает обработанный объект. S - универсальный параметр, хранит информацию об текущем
     *            обрабатываемом объекте
     */
    public static  <S> S fillData (S object, List<Object> values){
        return obtainMetadataOfObject(object, values);
    }

    /**
     * Извлечение метаданных из полученного объекта, для дальнейшей обработки
     * @param object - обрабатываемый объект
     * @param values - коллекция значений, которые будут сгенерированы для полей обрабатываемого объекта
     * @param <S> - универсальный параметр, указывает на тип данных передаваемого объекта
     * @return - возвращает обработанный объект. S - универсальный параметр, хранит информацию об текущем
     * обрабатываемом объекте
     */
    private static <S> S obtainMetadataOfObject(S object, List<Object> values){
        Class<?> objectClass = object.getClass();
        Class superClazz = objectClass.getSuperclass();
        String nameObjectClass = "Object";
        String simpleNameObjectExpected = superClazz.getSimpleName();
        boolean isObjectClass = Objects.equals(simpleNameObjectExpected, nameObjectClass);

        if(!isObjectClass){
           return manageParseListArrFields( object, values, objectClass);
        }
        return manageParseArrFields( object, values, objectClass);
    }
.......

" .. Если бы это был контроллер, тогда аннотация @Valid указывала бы на то, что прежде чем передать разобранный json в dto, то нужно произвести валидацию .. " - Я наполняю объект dto с помощью рефлексии и аннотация @Valid, в методах где используется рефлексия и разбираются поля объекта, - не работает.

Здесь не применим подход, когда используется @Valid при обработке запроса в rest-контроллере и где эта аннотация ставиться в параметрах метода, обрабатывающего запрос

Возможно, из-за того, что сущность имеет аннотацию , согласно которой Spring при инициализации Application сontext создал bean данного класса. А в случае с dto, валидация происходит в момент, когда контроллер обрабатывает запрос, чтобы передать данные из json в dto-объект и в момент передачи данных, аннотация @Valid указывает на то, что нужно обратиться к полям, которые имееют аннотации-ограничения и провалидировать добавляемые данные.

Но в моем случае, данные в dto добавляются в тестовых методах.

Oбновление (частичное решение)

Нужно указать аннотацию @Validated. Эта аннотация от Spring и тогда Spring при обращение к объектам типа dto (на уровне бизнес-логики) валидирует добавление значений.

Но это не работает почему то для объекта, который разделен на несколько классов. Хотя такого рода объект (уровень dao - где сущности расположены) валидируется спокойно, но там уже Hibernate управляет этим процессом.

Я же не использую клиента.

Я написал тесты, которые проверяют создание записи в таблицу, обращаясь на сервис-слой.

На сервис-слое я формирую объект с помощью рефлексии, чтобы 30 полей не собирать вручную. Я поставил проверку на аннотацию Size, чтобы вытянуть размер ограничения. И хоть я поставил аннотацию @Validated, я не получаю нужных данных. @Validated хорошо работает для отдельных классов, но у меня таблица разбита на несколько абстрактных и один производный и хоть над ними стоят аннотации @Validated , в этом случае Spring не видит что нужно валидировать данные поля.

Не срабатывает validation для DTO, почему ?

Answer 1

Для валидации строки можно использовать аннотацию @Length(max = 255)

@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class AuthRq extends BaseRq {
  @JsonProperty("username")
  @NotEmpty
  @Length(max = 255)
  private String username;
  @JsonProperty("password")
  @NotEmpty
  @Length(max = 255)
  private String password;
}

// Controller ==================================

@RequestMapping(value = AUTH_URL, method = RequestMethod.POST)
    public ResponseEntity<AuthRs> createAuthenticationToken(@RequestBody @Valid AuthRq authenticationRequest) throws Exception {
    ....
    }