#java #spring #spring-boot #validation
#java #spring #весенняя загрузка #проверка
Вопрос:
У меня есть проект Spring Boot (2.3.3), где я хочу проверить входные параметры методов сервисного уровня. Итак, в моем pom.xml Я добавил
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
поскольку она больше не является частью родительской. Далее у меня есть интерфейс моего метода обслуживания и реализующий метод service. Моя служба реализации помечена символом @Validated, а мой метод выглядит следующим образом
public void deleteGreetingById(@NotNull(message = "greetingId must not be null.")Integer greetingId) {
Я также читал, что проверка по умолчанию привязана только к уровню контроллера. Поэтому, чтобы включить ее также для уровня servie, я добавил PostValidationProcesser.
@Configuration
public class MethodValidationConfig {
@Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
return new MethodValidationPostProcessor();
}
}
Когда я теперь выполняю свой тест с null в качестве входного параметра, ничего не происходит, и исключение не генерируется. Когда я делаю
Assert.notNull(greetingId,"greetingId must not be null");
внутри метода, как и ожидалось, выдается исключение InvalidParameterException. Но я бы предпочел проверку на основе аннотаций из-за проверки @Valid объектов всего класса в качестве входного параметра.
Можно ли объяснить, почему проверка не запускается?
Редактировать:
@RestController
public class GreetingsConsumerController {
private final GreetingsService greetingsService;
public GreetingsConsumerController(GreetingsService greetingsService) {
this.greetingsService = greetingsService;
}
@PostMapping(value = "/greetings", consumes = MediaType.APPLICATION_JSON_VALUE)
public Greeting createGreeting( @RequestBody @Valid GreetingDto greetingDto){
return greetingsService.addGreeting(greetingDto);
}
@GetMapping(value = "/greetings/{id}")
public Greeting getGreetingById(@PathVariable Integer id){
return greetingsService.findGreetingById(id);
}
@GetMapping(value = "/greetings")
public List<Greeting> getAllGreetings(){
return greetingsService.findAllGreetings();
}
@DeleteMapping(value = "/greetings/{id}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void deleteGreetingById(@PathVariable Integer id){
greetingsService.deleteGreetingById(id);
}
}
Интерфейс:
public interface GreetingsService {
Greeting findGreetingById(Integer greetingId);
List<Greeting> findAllGreetings();
Greeting addGreeting( GreetingDto greetingDto);
void deleteGreetingById( Integer greetingId);
}
IterfaceImpl:
@Service
@Validated
public class GreetingsServiceImpl implements GreetingsService {
.
.
.
@Override
public void deleteGreetingById(@NotNull(message = "greetingId must not be null. ") Integer greetingId) {
...
}
}
Я также добавил компонент в свое SpringBootApplication, но по-прежнему не генерируется исключение.
@SpringBootApplication
public class GreetingsConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(GreetingsConsumerApplication.class, args
);
}
@Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
return new MethodValidationPostProcessor();
}
}
Комментарии:
1. Как вы вызываете метод? И как выглядит класс, в который вы добавили ограничение NotNull?
2. Я вызываю метод, как и любой другой общий метод. Значение NotNull в моем примере связано с одним параметром (как вы можете видеть в моем примере, параметром является greetingId). Проверка моих ограничений класса работает так, как ожидалось для DTO ввода контроллером. У меня просто не работает, когда я пытаюсь проверить параметры на уровне сервиса.
3. Вы используете Spring Boot, это
MethodValidationConfig
распознается Spring Boot? Как вы вызываете этотdeleteGreetingById
метод? Если это вызывается из того же класса, это не сработает.4. @M.Deinum У меня есть конечная точка rest, где я получаю запрос. Затем я извлекаю идентификатор и вызываю метод через экземпляр интерфейса. На самом деле я не знаю, выбран ли этот MethodvalidationConfig. Я попытаюсь установить точку останова. Я получил это решение из приведенного здесь примера baeldung.com/javax-validation-method-constraints
5. Он должен быть в пакете, охватываемом Spring Boot, иначе вам нужно его импортировать (или просто добавить
@Bean
метод в ваш@SpringBootApplication
аннотированный класс, что, вероятно, проще).
Ответ №1:
Ниже приведен пример проверки модели на уровне сервиса.
class TestModel{
@NotNull
private String name;
}
TestModel model= new TestModel();
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
Set<ConstraintViolation<TestModel>> violations = validator.validate(model);
Комментарии:
1. Проверка моей модели работает, когда запрос попадает на контроллер. Почему необходимо создавать отдельный валидатор? Я думаю, это должно работать так же, как на уровне контроллера?
2. Насколько мне известно, это не работает на уровне сервиса, для этого нам нужно написать отдельный валидатор.
Ответ №2:
Я «решил» проблему. Моя ошибка заключалась в том, что я неправильно настроил свои тесты. Я настроил тест с
@Extendwith(SpringExtension.class)
поскольку раньше я писал только модульные тесты без использования контекста в этом классе. Очевидно, что при использовании проверки параметров таким образом, вы должны использовать контекст, который превращает весь сценарий в интеграционный тест. Я рад, что это работает сейчас, и прошу прощения за ненужные обсуждения. Я должен был опубликовать свой тест также в коде.
Хотя я рад, что это работает сейчас, я также немного сбит с толку. В общем случае я не хочу запускать контекст Spring только для проверки ограничений. Но это другой вопрос.
Ответ №3:
Когда у вас есть службы, реализующие интерфейсы, и вы ссылаетесь на интерфейс, вам нужны аннотации проверки в интерфейсе, а не реализующий класс. Добавьте аннотации проверки в интерфейс GreetingsService.
Комментарии:
1. Я добавил это также в интерфейс и в оба. Ничего не происходит
2. Вы также добавили аннотацию @Validated в интерфейс?
3. Да, я пробовал это на интерфейсе, службе и на обоих.
4. Для моих приложений Boot 2.3 мне не нужно создавать компонент MethodValidationPostProcessor, как это делаете вы, и проверка метода работает. Для простых приложений Spring я создаю постпроцессор проверки метода и устанавливаю для его фабрики проверки значение LocalValidatorFactoryBean.
5. Я пробовал это без thisprocessor, но ничего не изменилось. Метод по-прежнему выполняется с нулевым объектом, и исключение не генерируется