В каком слое должна быть валидация?

18

Я создаю Rest API с помощью Spring Boot и использую Hibernate Validation для проверки входных данных запроса.

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

Должна ли эта проверка располагаться на уровне службы или на уровне контроллера?

Уровень обслуживания:

 public Company update(Company entity) {
    if (entity.getId() == null || repository.findOne(entity.getId()) == null) {
        throw new ResourceNotFoundException("can not update un existence data with id : " 
            + entity.getId());
    }
    return repository.saveAndFlush(entity);
}

Уровень контроллера:

public HttpEntity<CompanyResource> update(@Valid @RequestBody Company companyRequest) {
    Company company = companyService.getById(companyRequest.getId());
    Precondition.checkDataFound(company, 
        "Can't not find data with id : " + companyRequest.getId());

    // TODO : extract ignore properties to constant

    BeanUtils.copyProperties(companyRequest, company, "createdBy", "createdDate",
            "updatedBy", "updatedDate", "version", "markForDelete");
    Company updatedCompany = companyService.update(company);
    CompanyResource companyResource = companyAssembler.toResource(updatedCompany);
    return new ResponseEntity<CompanyResource>(companyResource, HttpStatus.OK);
}
fdarmanto
источник

Ответы:

8

Как уровень контроллера, так и уровень обслуживания предоставляют определенные интерфейсы. Интерфейсы определяют контракты о том, как интерфейс должен использоваться. Контракт обычно означает, какие аргументы (и их типы и значения) ожидаются, какие исключения могут быть выданы, какие побочные эффекты создаются и т. Д.

Теперь ваша валидация - это, по сути, принудительное выполнение контракта метода update () контроллера и метода update () уровня обслуживания. У обоих из них очень похожий контракт, поэтому было бы естественно, если бы валидация (обеспечение выполнения контракта) была бы тоже распространенной.

Один из возможных способов сделать это - отделить валидацию этого контракта и вызвать его на обоих уровнях. Обычно это наиболее очевидно - каждый класс / метод обеспечивает выполнение своего собственного контракта, но часто это непрактично из-за производительности (доступа к базе данных) или по другим причинам.

Другая возможность состоит в том, чтобы делегировать эту проверку на уровень обслуживания, в то же время явно определяя поведение в случае неудачной проверки в контракте уровня обслуживания. Сервисный уровень обычно возвращает некоторую общую ошибку проверки (или исключение выброса), а уровень контроллера захочет как-то определенным образом отреагировать на ошибку - в этом случае мы возвратим 400 неверных запросов, чтобы указать, что входящий запрос недействителен.

В этом проекте существует опасность слишком сильной связи между бизнес-логикой на уровне обслуживания (которая должна быть довольно общей) и контроллером (который обрабатывает логику интеграции).

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

QBD
источник
1

Вход должен быть проверен в слое обслуживания.

И «Не удается найти идентификатор» является логической ошибкой. Так что следует выбросить из слоя контроллера.

Это снова зависит от вашего слоя / дизайна.
Что должен делать сервисный уровень и что ожидается от уровня контроллера.

безбумажный
источник
Ответ не должен искать дополнительного разъяснения от вопроса. Если вопрос требует уточнения, его следует прокомментировать и, возможно, пометить для закрытия, если он слишком неясен. Да, я понимаю, что у вас нет репутации ни для одного из этих действий.
«Проверка ввода» неоднозначна. Например, я мог бы поместить атрибут Required в поле, чтобы указать, что оно должно быть заполнено, но также я мог бы поставить сложный настраиваемый атрибут, который проверяет, например, что одно значение поля больше другого. ИМХО, проверка Сравнения "пахнет" гораздо больше уровня бизнес-сервисов, чем уровень контроллера.
JustAMartin
1

Проверки гибернации - это проверка целостности данных. Чтобы избежать RuntimeExceptions от bbdd. Это почти те же проверки, которые вы должны контролировать с помощью Constrains . Поскольку только постоянный уровень должен подпитывать постоянный уровень, вы можете (или не можете сами) доверять правильности данных, поступающих с вашего бизнес-уровня

Я не помещаю проверки в DAO. Я ожидаю достоверных данных из верхних слоев. В случае ошибки я делегирую bbdd ответственность знать о его содержимом.

Затем идет проверка на бизнес-уровне. Все проверки бизнеса были направлены на поддержание согласованности данных, а не их целостности .

Наконец, я делаю предыдущие проверки на контрольном слое. Те, которые связаны только с таким слоем.

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

Иногда контроллеры имеют свои собственные правила и условия, которые не будут воспроизведены на любом другом фасаде. Тогда это кандидат, который будет помещен в такой контроллер.

Подумайте о том, что вы подтверждаете, и если вы хотите применить его для всех, несмотря ни на что. Или, если это контекстная проверка («Я проверяю то, что происходит только на определенном фасаде элемента управления / представления).

LAIV
источник
0

В нашем магазине Java мы специально разделили проверку веб-виджетов на три отдельные операции.

  1. Основное форматирование - числа должны быть числами; даты должны быть действительными датами и т. д. Обычно эта проверка выполняется бесплатно - веб-платформа сделает это за вас при привязке содержимого виджета к модели.
  2. Проверка одного виджета - дата должна быть в прошлом; целое число должно быть от 1 до 100; customerId должен существовать в базе данных и т. д. В большинстве случаев это относится к уровню контроллера, но может потребоваться поддержка из хранилища данных.
  3. Проверка кросс-виджета - дата оформления заказа должна быть позже даты заезда; дата смерти не может быть раньше даты рождения и т. д. Это, безусловно, проверка бизнес-правил. Мы также склонны помещать это на уровень контроллера, но вы можете захотеть превратить его в бизнес-валидатор, чтобы его можно было использовать повторно.

Если происходит сбой 1-го уровня, мы не проверяем 2 или 3. Аналогично, если 1 успешен, а 2 не выполняется, мы не делаем 3. Это останавливает генерирование ложных сообщений об ошибках.

Вы спрашиваете о значениях в вызове REST, а не о содержимом виджета, но применяются те же принципы.

kiwiron
источник
-1

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

Ханс Пу
источник