Проверка в Spring MVC в контроллерах или уровне обслуживания?

В течение некоторого времени я пытаюсь выяснить, где валидация пользовательского ввода должна происходить в приложении Spring MVC. Во многих онлайн-блогах и учебниках я в основном читаю, что контроллер должен проверять ввод пользователей и, если он недействителен, отвечать на запрос пользователя, показывая страницу с сообщением об ошибке. Однако мое нынешнее понимание системы слоев Spring и Spring MVC состоит в том, что контроллер является единственным мелким интерфейсом между логикой приложения (уровнем обслуживания) и "веб-миром", что позволяет использовать сервисный уровень из веб. Кроме того, насколько я вижу, Spring MVC предоставляет только разумные инструменты для проверки в контроллере.

Если теперь проверка выполняется в контроллере, если в какой-то момент я хочу развязать логику приложения из "веб-мира", логика проверки должна быть переопределена в новой среде (например, настольном приложении с использованием Swing). На мой взгляд, способность решать, какие операции являются "действительными" для объектов домена и какие "правильные" состояния могут иметь такие объекты, является основной частью уровня сервиса, а не проблемой какой-либо другой части приложения (например, Контроллеры).

В этом контексте, почему "хорошей практикой" является размещение логики проверки ввода на уровне контроллера, а не на уровне сервиса?

Ответ 1

Общим подходом является проверка достоверности в обоих местах. Но если вы говорите о @Valid, по моему опыту лучше надеть уровень контроллеров.

Это также зависит от того, какую логику проверки мы говорим. Скажем, у вас есть bean:

@Data
public class MyBean {
    @NotNull private UUID someId;
    @NotEmpty private String someName; 
}

Было бы разумно, чтобы этот bean был аннотирован с помощью @Valid на уровне контроллера, чтобы он даже не доходил до службы. Нет смысла использовать @Valid для метода службы, потому что, почему бы вам распространить его дальше, пока вы можете сразу же в контроллере решить, является ли это допустимым или нет.

Затем существует второй тип проверки: проверка бизнес-логики. Скажем, для того же bean, что свойство someId является timeUUid, и его метка времени должна быть не более 2 дней после какого-либо события, в противном случае bean должен быть отброшен службой.

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

Поскольку оба подхода к валидации действительно подтверждают разные вещи, очевидно, что каждый из ваших компонентов MVC - Model, View и Controller - выполняет свою собственную проверку, и он должен быть разумным в отношении того, что он проверяет, не вводя зависимость с другой компоненты.

Что касается отображения ошибки пользователю, да, объект Errors действительно предназначен для использования для проверки bean при уровень контроллера, но вы можете создать какой-то фильтр, который ловит исключения на любом уровне, а затем довольно форматирует его для пользователя. Есть много подходов к этому, и я не уверен, что Spring предписывает, что любой лучше, чем другой.

В зависимости от механизма разрешения (как, например, jstl или jackson или что-то еще), , вероятно, будет склонен рассматривать проверку другим способом. Например, традиционный jstl view resolver будет хорошо работать с устройством, использующим ошибки, а jackson resolver, вероятно, будет работать лучше с комбинацией @ResponseBody и фильтра, который улавливает ошибки и помещает их в предопределенная часть ошибки объекта ответа.

Ответ 2

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

myview <-> MyController <- MyService <- MyDAO
                 ^
                 |
               MyHelper

Controllers обрабатывали разрешение представления.
Services обрабатывают отображение из dto-s в модели объектов для просмотра и наоборот,
DAO-s обрабатывает транзакции базы данных и,
Helpers обрабатывали все остальное, включая проверку.

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