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


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);
}

Ответы:


8

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

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

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

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

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

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


1

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

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

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


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

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

1

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

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

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

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

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

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

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


0

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

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

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

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


-1

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.