Проверьте предварительные условия на контроллере или на уровне обслуживания

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

@Controller
...
public void register(ProductInfo data) {
    Preconditions.checkArgument(StringUtils.hasText(data.getName()),
        "Empty name parameter.");
    productService.register(data);
}

@Service
...
public void register(ProductInfo data) {
    productDao.register(data);
}

Но я думал, что метод register на уровне службы будет использовать другой метод контроллера, как показано ниже:

@Controller
...
public void register(ProductInfo data) {
    productService.register(data);
}
public void anotherRegister(ProductInfo data) {
    productService.register(data);
}

@Service 
...
public void register(ProductInfo data) {
    Preconditions.checkArgument(StringUtils.hasText(data.getName()),
        "Empty name parameter.");
    productDao.register(data);
}

С другой стороны, метод уровня сервиса будет использоваться только в одном контроллере.
Я был смущен. Каков лучший способ проверки предусловий в контроллере или сервисе?
Заранее спасибо.

Ответ 1

В идеале вы бы сделали это в обоих местах. Но ваши сбивающие с толку две разные вещи:

  • Проверка (с обработкой ошибок)
  • Defensivie Programming (aka assertions, aka design by contract).

Вы абсолютно должны выполнить проверку в контроллере и защитное программирование в своей службе. И вот почему.

Для форм и запросов REST вам необходимо проверить, чтобы вы могли отправить разумную ошибку клиенту. Это включает в себя то, что поля плохие, а затем делает локализацию сообщений об ошибках... и т.д. (Ваш текущий пример отправил бы мне ужасное сообщение об ошибке 500 с трассировкой стека, если свойство ProductInfo.name было null).

Spring имеет решение для проверки объектов в контроллере.

Оборонительное программирование выполняется на уровне сервиса BUT NOT валидация, потому что у вас нет доступа к языку для генерации правильных сообщений об ошибках. Некоторые люди, но Spring действительно не помогают вам там.

Другая причина, по которой валидация не выполняется на уровне службы, заключается в том, что ORM уже обычно это делает с помощью спецификации JSR Bean Validation (спящий режим), но она не генерирует разумных сообщений об ошибках.

Одной из задач стратегии является создание там собственной библиотеки utconditions, которая генерирует нестандартные производные RuntimeException вместо guava (и commons lang) IllegalArgumentException и IllegalStateException, а затем try... catch исключения в контроллере, преобразуя их в сообщения об ошибках проверки.

Ответ 2

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

Другое возможное решение: используйте Bean Validation (JSR-303), чтобы поставить проверки (предварительные условия) на сам ProductInfo Bean. Таким образом, вы укажете только один раз, и все, что нужно, может быстро проверить bean.

Ответ 3

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

@controller
public class MyController{

@ExceptionHandler(MyDataIntegrityExcpetion.class)
public String handleException(MyDataIntegrityExcpetion ex, HttpServletRequest request) {
  //do someting on exception or return some view. 
}

}

Это также зависит от того, что вы делаете в контроллере. возвращаете ли вы View или просто используете @ResponseBody Annotation. Spring MVC имеет хорошее решение "из коробки" для проверки ввода/вывода. Я рекомендую вам проверить эти библиотеки.

http://static.springsource.org/spring/docs/3.1.x/spring-framework-reference/html/validation.html

Ответ 4

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

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

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

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