Person

Проверка данных — Java & Spring Validation

Проверка данных класса (bean) в java тема не новая, но актуальная и здесь я объединю различные аспекты: валидацию данных в рамках JSR-303, покажу как это сделать чисто в Java и с использованием Spring, как делать в стандартном приложении и в Web.

Содержание: Валидация данных (JSR-303) в

  • стандартном Java приложении
  • c использованием Spring
  • объединение Java + Spring
  • Spring MVC

Validation в стандартном Java приложении

Для проверки объекта используются аннотации на полях класса, т.е. декларативная модель. Аннотации есть уже готовые:

а также можно делать и собственные. И так есть класс (bean)

import javax.validation.constraints.Digits; import javax.validation.constraints.Size; public class Person < @Size(min=2, max=50) private String Name; @Digits(integer=3, fraction=0, message = "Не более 3-х знаков") @PersonAgeConstraint private Integer age; public Person(String name, Integer age) < Name = name; this.age = age; >> 

Здесь в примере Size и @Digits готовые аннотации, а @PersonAgeConstraint собственная. Как сделать собственную:

@Target() @Retention(RetentionPolicy.RUNTIME) @Constraint(validatedBy=PersonAgeConstraintValidator.class) public @interface PersonAgeConstraint < String message() default ""; Class[] groups() default <>; Class[] payload() default <>; > 

Пример реализации HealthConstraintValidator

public class HealthConstraintValidator implements ConstraintValidator  < @Override public boolean isValid(Documents documents, ConstraintValidatorContext constraintValidatorContext) < return documents.contains("справка 1"); >> 

для ProfessionalConstraint все аналогично

 @Test public void healthAndProfessionalValidators() < final Person person = new Person("Иван Петров", 45); person.setHealthDocuments(new Documents(Arrays.asList("справка 1", "справка 3"))); person.setProfessionalDocuments(new Documents(Arrays.asList("тест 1", "тест 4"))); // проверка на здоровье Set> validates = validator.validate(person, Health.class); Assert.assertTrue(validates.size() == 0); // и если здоровье Ок, то проф. тест validates = validator.validate(person, Professional.class); Assert.assertTrue(validates.size() == 0); > 

Подобные проверки, например нужны когда мы загружаем данные из файла, web service и др. источников.

public class Documents < private Listtests = new ArrayList(); public Documents(List tests) < this.tests.addAll(tests); >public boolean contains(String test) < return this.tests.contains(test); >> 

Validation c использованием Spring

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

Переопределив два метода, делаем валидацию

@Service public class PersonValidator implements Validator < @Override public boolean supports(ClassaClass) < return Person.class.equals(aClass); >@Override public void validate(Object obj, Errors errors) < Person p = (Person) obj; if (p.getAge() < 0) < errors.rejectValue("age", "value.negative"); >> > 

value.negative — так же является ключом в файле сообщений, public boolean supports определяет тип поддерживаемого класса.

Проверка запускается через DataBinder

@RunWith(SpringRunner.class) @SpringBootTest public class DemoJValidationApplicationTests < // указываем файл сообщений private static final ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource(); static < messageSource.setBasename("message"); >@Autowired private PersonValidator personValidator; @Test public void testValidators() < final Person person = new Person("Иван Петров", -4500); final DataBinder dataBinder = new DataBinder(person); dataBinder.addValidators(personValidator); dataBinder.validate(); Assert.assertTrue(dataBinder.getBindingResult().hasErrors()); if (dataBinder.getBindingResult().hasErrors()) < dataBinder.getBindingResult().getAllErrors().stream(). forEach(e ->System.out.println(messageSource .getMessage(e, Locale.getDefault()))); > > > 

Будут выполнены все проверки которые имплементировали org.springframework.validation.Validator для класса Person.

Можно добавить так же несколько валидаторов, dataBinder.addValidators, можно сделать композицию правил (вызов из одного правила, другого), пример:

public class OtherValidator implements Validator < @Override public boolean supports(ClassaClass) < return Person.class.equals(aClass); >@Override public void validate(Object obj, Errors errors) < // . >> //--------- @Service public class PersonValidator implements Validator < /** * другое правила */ @Autowired private OtherValidator otherValidator; @Override public void validate(Object obj, Errors errors) < Person p = (Person) obj; if (p.getAge() < 0) < errors.rejectValue("age", "value.negative"); >// из одного правила, вызываем другое otherValidator.validate(obj, errors); > > 

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

image

  4.0.0 com.example DemoJSRvalidation 0.0.1-SNAPSHOT jar DemoJSRvalidation Demo project for Spring Boot JSR-303 validation org.springframework.boot spring-boot-starter-parent 2.0.5.RELEASE  UTF-8 UTF-8 1.8   org.springframework.boot spring-boot-starter-validation  org.springframework.boot spring-boot-starter-test test     org.springframework.boot spring-boot-maven-plugin     

Java & Spring

Очевидно я захочу использовать два подхода в проверки данных — Java и Spring, объединить их можно, а именно добавить в Spring validator вызов javax.validation.Validator.

import javax.validation.Validator; @Service public class PersonValidator implements org.springframework.validation.Validator < // javax.validation.Validator @Autowired private Validator validator; @Override public boolean supports(ClassaClass) < return Person.class.equals(aClass); >@Override public void validate(Object obj, Errors errors) < Set> validates = validator.validate(obj); for (ConstraintViolation constraintViolation : validates) < String propertyPath = constraintViolation.getPropertyPath().toString(); String message = constraintViolation.getMessage(); errors.rejectValue(propertyPath, "", message); >Person p = (Person) obj; if (p.getAge() < 0) < errors.rejectValue("age", "only.positive.numbers"); >> > 

С помощью spring делаем injection javax.validation.Validator

@Autowired
private Validator validator;

далее на методе public void validate(Object obj, Errors errors)
выполняем декларативные проверки java, а затем выполняем все проверки для класса Person на spring org.springframework.validation.Validator.

Запускаем проверку также через spring

 @Test public void testValidators() < final Person person = new Person("Иван", -4500); final DataBinder dataBinder = new DataBinder(person); dataBinder.addValidators(personValidator); dataBinder.validate(); if (dataBinder.getBindingResult().hasErrors()) < dataBinder.getBindingResult().getAllErrors() // . 

Теперь в коллекции будут проверки от аннотаций java и spring (org.springframework.validation.Validator) для Person

Отрицательное значение (аннотация)
Не более 3-х знаков (аннотация)
Только положительные число (spring)

image

Spring MVC

Конечно теперь это все можно применить в web приложении.

Добавляем в проект Controller, jsp страницу (тут кстати могут и другие варианты, например генерация страниц с помощью freeMarker, и др.), css стиль, pom зависимость. И так по порядку

import org.springframework.validation.Validator; @Controller public class DemoJValidationController < @Autowired @Qualifier("personValidator") // spring validator private Validator personValidator; @InitBinder protected void initBinder(WebDataBinder binder) < binder.setValidator(personValidator); >@GetMapping("/") public String savePersonAction(ModelMap model) < model.addAttribute("person", new Person(null, null)); return "personEdit"; >@RequestMapping(value = "/save", method = RequestMethod.POST) public String savePersonAction( @Valid @ModelAttribute("person") Person person, BindingResult bindingResult, Model model) < if (bindingResult.hasErrors()) < return "personEdit"; // to person.jsp page >model.addAttribute("name", person.getName()); model.addAttribute("age", person.getAge()); return "saveSuccess"; // to saveSuccess.jsp page > @RequestMapping(value = "/edit", method = RequestMethod.POST) public String editPersonAction(ModelMap model) < model.addAttribute("person", new Person(null, null)); return "personEdit"; // to personEdit.jsp page; >> 

Здесь с помощью spring injection подключен PersonValidator

@Autowired
@Qualifier("personValidator") // spring validator
private Validator personValidator;

устанавливаем PersonValidator в initBinder

@InitBinder
protected void initBinder(WebDataBinder binder) binder.setValidator(personValidator);
>

Проверка инициируется с помощью аннотации @Valid
В этом случае выполнится только spring проверка, декларативные проверки будут проигнорированы.

@InitBinder
protected void initBinder(WebDataBinder binder)

то наоборот выполнятся все декларативные проверки, а spring будут проигнорированы.
Что бы выполнить все проверки и декларативные и spring, можно поступить так:

Убрать @InitBinder, оставить injection

@Autowired
@Qualifier("personValidator") // spring validator
private Validator personValidator;

и добавить вызов spring проверки вручную

// spring validate
personValidator.validate(person, bindingResult);

@Controller public class DemoJValidationController < @Autowired @Qualifier("personValidator") // spring validator private Validator personValidator; //. @RequestMapping(value = "/save", method = RequestMethod.POST) public String savePersonAction( @Valid @ModelAttribute("person") Person person, BindingResult bindingResult, Model model) < // spring validate personValidator.validate(person, bindingResult); if (bindingResult.hasErrors()) < return "personEdit"; // to person.jsp page >model.addAttribute("name", person.getName()); model.addAttribute("age", person.getAge()); return "saveSuccess"; // to saveSuccess.jsp page > > 

т.е. в bindingResult будут добавлены еще проверки от spring :-), что и хотелось!

Привязка данных в jsp и модели, осуществляется атрибутом - modelAttribute="person" В примере подключена SpringMVC’s Form Tag Library.

Остальные ресурсы этого примера:

@SpringBootApplication @ImportResource("classpath:configuration.xml") public class DemoJValidationApplication < public static void main(String[] args) < SpringApplication.run(DemoJValidationApplication.class, args); >>
     " rel="stylesheet">    

Enter Person.

Name:
Age:
     " rel="stylesheet">    

Person Saved Successfully.

$ $
  4.0.0 com.example DemoJSRvalidation 0.0.1-SNAPSHOT jar DemoJSRvalidation Demo project for Spring Boot JSR-303 validation org.springframework.boot spring-boot-starter-parent 2.0.5.RELEASE  UTF-8 UTF-8 1.8   org.springframework.boot spring-boot-starter-validation  org.springframework.boot spring-boot-starter-test test  org.springframework.boot spring-boot-starter-web  org.hibernate hibernate-validator 4.1.0.Final  org.apache.tomcat.embed tomcat-embed-jasper  javax.servlet jstl     org.springframework.boot spring-boot-maven-plugin     

Источник

Читайте также:  Datetime now format php
Оцените статью