Важность валидации данных не требует пояснений, даже если валидация осуществляется на фронтенде, необходимо ещё раз провести валидацию данных, передаваемых на бэкенд, чтобы избежать ситуации, когда пользователи обходят браузер и отправляют незаконные запросы напрямую через HTTP-инструменты.
Наиболее распространённый подход выглядит примерно так. Мы используем if/else
-инструкции для проверки каждого параметра запроса.
@RestController
@RequestMapping("/api/person")
public class PersonController {
@PostMapping
public ResponseEntity<PersonRequest> save(@RequestBody PersonRequest personRequest) {
if (personRequest.getClassId() == null
|| personRequest.getName() == null
|| !Pattern.matches("(^Man$|^Woman$|^UGM$)", personRequest.getSex())) {
}
return ResponseEntity.ok().body(personRequest);
}
}
Такой код наверняка знаком многим разработчикам, и многие открытые проекты используют подобный подход для валидации входных параметров.
Однако, такой подход не рекомендуется, так как он явно нарушает принцип единой ответственности. Включение большого количества небизнес-логики в бизнес-логику делает код трудным для поддержки и усложняет бизнес-логику.
На самом деле, есть простые способы улучшить этот код, и это основная тема данной статьи.
Без лишних слов, ниже я приведу примеры, основанные на моем опыте работы над проектами, которые демонстрируют, как можно элегантно проводить валидацию параметров в приложениях Spring Boot (что применимо и к обычным Java-приложениям).Если вы не знакомы с этим, обязательно изучите, так как вы сможете сразу применить это к вашим проектам.
Кроме того, примеры кода в данной статье используют последнюю версию Spring Boot 2.4.5! (на момент 21 апреля 2021 года)
Адрес репозитория с примерами кода: https://github.com/CodingDocs/springboot-guide/tree/master/source-code/bean-validation-demo.
Если вы разрабатываете обычное Java-приложение, вам могут понадобиться следующие зависимости:
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.9.Final</version>
</dependency>
<dependency>
<groupId>javax.el</groupId>
<artifactId>javax.el-api</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>org.glassfish.web</groupId>
<artifactId>javax.el</artifactId>
<version>2.2.6</version>
</dependency>
Однако, я уверен, что большинство из вас использует Spring Boot для разработки.
Если вы используете Spring Boot, всё становится намного проще, вам достаточно добавить зависимость spring-boot-starter-web
, которая включает все необходимые зависимости. В нашем примере проекте также используется Lombok.
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
```## Валидация ввода контроллера
### Валидация тела запроса
Валидация тела запроса означает валидацию параметров методов, помеченных аннотацией `@RequestBody`.
**`PersonController`**
Мы добавили аннотацию `@Valid` к параметрам, которые нужно валидировать. Если валидация не пройдет, будет выброшено исключение `MethodArgumentNotValidException`. По умолчанию Spring преобразует это исключение в HTTP статус 400 (ошибка запроса).
```java
@RestController
@RequestMapping("/api/person")
@Validated
public class PersonController {
@PostMapping
public ResponseEntity<PersonRequest> save(@RequestBody @Valid PersonRequest personRequest) {
return ResponseEntity.ok().body(personRequest);
}
}
PersonRequest
Мы используем аннотации валидации для проверки параметров запроса!
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class PersonRequest {
@NotNull(message = "classId не может быть пустым")
private String classId;
@Size(max = 33)
@NotNull(message = "name не может быть пустым")
private String name;
@Pattern(regexp = "(^Man$|^Woman$|^UGM$)", message = "значение sex не находится в допустимом диапазоне")
@NotNull(message = "sex не может быть пустым")
private String sex;
}
Пояснение регулярных выражений:
^string
: Соответствует строкам, начинающимся с string
string$
: Соответствует строкам, заканчивающимся на string
^string$
: Точное соответствие строке string
(^Man$|^Woman$|^UGM$)
: Значение должно быть одним из Man
, Woman
, UGM
GlobalExceptionHandler
Пользовательский обработчик исключений может помочь нам поймать исключения и выполнить некоторые простые действия. Если вы не понимаете код обработки исключений ниже, вы можете ознакомиться с этой статьей «SpringBoot: несколько способов обработки исключений».
@ControllerAdvice(assignableTypes = {PersonController.class})
public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Map<String, String>> handleValidationExceptions(
MethodArgumentNotValidException ex) {
Map<String, String> errors = new HashMap<>();
ex.getBindingResult().getAllErrors().forEach((error) -> {
String fieldName = ((FieldError) error).getField();
String errorMessage = error.getDefaultMessage();
errors.put(fieldName, errorMessage);
});
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errors);
}
}
Проверка через тестирование
Ниже я использую MockMvc
для имитации запросов к Controller
для проверки, работает ли это. Конечно, вы также можете использовать инструменты, такие как Postman
, для проверки.```java
@SpringBootTest
@AutoConfigureMockMvc
public class PersonControllerTest {
@Autowired
private MockMvc mockMvc;
}
@Autowired
private ObjectMapper objectMapper;
/**
* Проверка случая, когда параметры недопустимы, выбрасывается异常并且可以正确被捕获
*/
@Test
public void should_check_person_value() throws Exception {
PersonRequest personRequest = PersonRequest.builder().sex("Man22")
.classId("82938390").build();
mockMvc.perform(post("/api/personRequest")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(personRequest)))
.andExpect(MockMvcResultMatchers.jsonPath("sex").value("sex значение не входит в допустимый диапазон"))
.andExpect(MockMvcResultMatchers.jsonPath("name").value("name не может быть пустым"));
}
}
```**Проверка с помощью Postman**```
```markdown
@Autowired
private ObjectMapper objectMapper;
/**
* Проверка случая, когда параметры недопустимы, выбрасывается异常并且可以正确被捕获
*/
@Test
public void should_check_person_value() throws Exception {
PersonRequest personRequest = PersonRequest.builder().sex("Man22")
.classId("82938390").build();
mockMvc.perform(post("/api/personRequest")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(personRequest)))
.andExpect(MockMvcResultMatchers.jsonPath("sex").value("sex значение не входит в допустимый диапазон"))
.andExpect(MockMvcResultMatchers.jsonPath("name").value("name не может быть пустым"));
}
}
```**Проверка с помощью Postman**```
```markdown
@Autowired
private ObjectMapper objectMapper;
/**
* Проверка случая, когда параметры недопустимы, выбрасывается异常并且可以正确被捕获
*/
@Test
public void should_check_person_value() throws Exception {
PersonRequest personRequest = PersonRequest.builder().sex("Man22")
.classId("82938390").build();
mockMvc.perform(post("/api/personRequest")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(personRequest)))
.andExpect(MockMvcResultMatchers.jsonPath("sex").value("sex значение не входит в допустимый диапазон"))
.andExpect(MockMvcResultMatchers.jsonPath("name").value("name не может быть пустым"));
}
}
```**Проверка с помощью Postman**```
```markdown
@Autowired
private ObjectMapper objectMapper;
/**
* Проверка случая, когда параметры недопустимы, выбрасывается异常并且可以正确被捕获
*/
@Test
public void should_check_person_value() throws Exception {
PersonRequest personRequest = PersonRequest.builder().sex("Man22")
.classId("82938390").build();
mockMvc.perform(post("/api/personRequest")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(personRequest)))
.andExpect(MockMvcResultMatchers.jsonPath("sex").value("sex значение не входит в допустимый диапазон"))
.andExpect(MockMvcResultMatchers.jsonPath("name").value("name не может быть пустым"));
}
}
```**Проверка с помощью Postman**```
```markdown
@Autowired
private ObjectMapper objectMapper;
/**
* Проверка случая, когда параметры недопустимы, выбрасывается异常并且可以正确被捕获
*/
@Test
public void should_check_person_value() throws Exception {
PersonRequest personRequest = PersonRequest.builder().sex("Man22")
.classId("82938390").build();
mockMvc.perform(post("/api/personRequest")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(personRequest)))
.andExpect(MockMvcResultMatchers.jsonPath("sex").value("sex значение не входит в допустимый диапазон"))
.andExpect(MockMvcResultMatchers.jsonPath("name").value("name не может быть пустым"));
}
}
```**Проверка с помощью Postman**```
```markdown
@Autowired
private ObjectMapper objectMapper;
/**
* Проверка случая, когда параметры недопустимы, выбрасывается异常并且可以正确被捕获
*/
@Test
public void should_check_person_value() throws Exception {
PersonRequest personRequest = PersonRequest.builder().sex("Man22")
.classId("82938390").build();
mockMvc.perform(post("/api/personRequest")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(personRequest)))
.andExpect(MockMvcResultMatchers.jsonPath("sex").value("sex значение не входит в допустимый диапазон"))
.andExpect(MockMvcResultMatchers.jsonPath("name").value("name не может быть пустым"));
}
}
```**Проверка с помощью Postman**```
```markdown
@Autowired
private ObjectMapper objectMapper;
/**
* Проверка случая, когда параметры недопустимы, выбрасывается异常并且可以正确被捕获
*/
@Test
public void should_check_person_value() throws Exception {
PersonRequest personRequest = PersonRequest.builder().sex("Man22")
.classId("82938390").build();
mockMvc.perform(post("/api/personRequest")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(personRequest)))
.andExpect(MockMvcResultMatchers.jsonPath("sex").value("sex значение не входит в допустимый диапазон"))
.andExpect(MockMvcResultMatchers.jsonPath("name").value("name не может быть пустым"));
}
}
```**Проверка с помощью Postman**```
```markdown
@Autowired
private ObjectMapper objectMapper;
/**
* Проверка случая, когда параметры недопустимы, выбрасывается异常并且可以正确被捕获
*/
@Test
public void should_check_person_value() throws Exception {
PersonRequest personRequest = PersonRequest.builder().sex("Man22")
.classId("82938390").build();
mockMvc.perform(post("/api/personRequest")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(personRequest)))
.andExpect(MockMvcResultMatchers.jsonPath("sex").value("sex значение не входит в допустимый диапазон"))
.andExpect(MockMvcResultMatchers.jsonPath("name").value("name не может быть пустым"));
}
}
```**Проверка с помощью Postman**```
```markdown
@Autowired
private ObjectMapper objectMapper;
/**
* Проверка случая, когда параметры недопустимы, выбрасывается异常并且可以正确被捕获
*/
@Test
public void should_check_person_value() throws Exception {
PersonRequest personRequest = PersonRequest.builder().sex("Man22")
.classId("82938390").build();
mockMvc.perform(post("/api/personRequest")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(personRequest)))
.andExpect(MockMvcResultMatchers.jsonPath("sex").value("sex значение не входит в допустимый диапазон"))
.andExpect(MockMvcResultMatchers.jsonPath("name").value("name не может быть пустым"));
}
}
```**Проверка с помощью Postman**```
```markdown
@Autowired
private ObjectMapper objectMapper;
/**
* Проверка случая, когда параметры недопустимы, выбрасывается异常并且可以正确被捕获
*/
@Test
public void should_check_person_value() throws Exception {
PersonRequest personRequest = PersonRequest.builder().sex("Man22")
.classId("82938390").build();
mockMvc.perform(post("/api/personRequest")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(personRequest)))
.andExpect(MockMvcResultMatchers.jsonPath("sex").value("sex значение не входит в допустимый диапазон"))
.andExpect(MockMvcResultMatchers.jsonPath("name").value("name не может быть пустым"));
}
}
```**Проверка с помощью Postman**```
```markdown
@Autowired
private ObjectMapper objectMapper;
/**
* Проверка случая, когда параметры недопустимы, выбрасывается异常并且可以正确被捕获
*/
@Test
public void should_check_person_value() throws Exception {
PersonRequest personRequest = PersonRequest.builder().sex("Man22")
.classId("82938390").build();
mockMvc.perform(post("/api/personRequest")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(personRequest)))
.andExpect(MockMvcResultMatchers.jsonPath("sex").value("sex значение не входит в допустимый диапазон"))
.andExpect(MockMvcResultMatchers.jsonPath("name").value("name не может быть пустым"));
}
}
```**Проверка с помощью Postman**```
```markdown
@Autowired
private ObjectMapper objectMapper;
/**
* Проверка случая, когда параметры недопустимы, выбрасывается异常并且可以正确被捕获
*/
@Test
public void should_check_person_value() throws Exception {
PersonRequest personRequest = PersonRequest.builder().sex("Man22")
.classId("82938390").build();
mockMvc.perform(post("/api/personRequest")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(personRequest)))
.andExpect(MockMvcResultMatchers.jsonPath("sex").value("sex значение не входит в допустимый диапазон"))
.andExpect(MockMvcResultMatchers.jsonPath("name").value("name не может быть пустым"));
}
}
```**Проверка с помощью Postman**```
```markdown
@Autowired
private ObjectMapper objectMapper;
/**
* Проверка случая, когда параметры недопустимы, выбрасывается异常并且可以正确被捕获
*/
@Test
public void should_check_person_value() throws Exception {
PersonRequest personRequest = PersonRequest.builder().sex("Man22")
.classId("82938390").build();
mockMvc.perform(post("/api/personRequest")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(personRequest)))
.andExpect(MockMvcResultMatchers.jsonPath("sex").value("sex значение не входит в допустимый диапазон"))
.andExpect(MockMvcResultMatchers.jsonPath("name").value("name не может быть пустым"));
}
}
```**Проверка с помощью Postman**```
```markdown
@Autowired
private ObjectMapper objectMapper;
/**
* Проверка случая, когда параметры недопустимы, выбрасывается异常并且可以正确被捕获
*/
@Test
public void should_check_person_value() throws Exception {
PersonRequest personRequest = PersonRequest.builder().sex("Man22")
.classId("82938390").build();
mockMvc.perform(post("/api/personRequest")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(personRequest)))
.
### Проверка параметров запроса
Проверка параметров запроса (Path Variables и Request Parameters) включает проверку методов, помеченных аннотациями `@PathVariable` и `@RequestParam`.
**`PersonController`**
**Не забудьте добавить аннотацию `@Validated` на уровне класса, чтобы Spring мог выполнять валидацию параметров методов.**
```java
@RestController
@RequestMapping("/api/persons")
@Validated
public class PersonController {
@GetMapping("/{id}")
public ResponseEntity<Integer> getPersonByID(@Valid @PathVariable("id") @Max(value = 5, message = "Превышено допустимое значение id") Integer id) {
return ResponseEntity.ok().body(id);
}
@PutMapping
public ResponseEntity<String> getPersonByName(@Valid @RequestParam("name") @Size(max = 6, message = "Превышено допустимое значение name") String name) {
return ResponseEntity.ok().body(name);
}
}
```
**`ExceptionHandler`**
```java
@ExceptionHandler(ConstraintViolationException.class)
ResponseEntity<String> handleConstraintViolationException(ConstraintViolationException e) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage());
}
```
**Проверка с помощью тестов**
```java
@Test
public void should_check_path_variable() throws Exception {
mockMvc.perform(get("/api/person/6")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isBadRequest())
.andExpect(content().string("getPersonByID.id: Превышено допустимое значение id"));
}
@Test
public void should_check_request_param_value2() throws Exception {
mockMvc.perform(put("/api/person")
.param("name", "snailclimbsnailclimb")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isBadRequest())
.andExpect(content().string("getPersonByName.name: Превышено допустимое значение name"));
}
```**Проверка с помощью Postman**


## Проверка методов в сервисе
Можно также выполнять валидацию входных данных для любого Spring Bean, а не только для параметров контроллеров. Это можно сделать с помощью комбинации аннотаций `@Validated` и `@Valid`. В общем случае, мы предпочитаем использовать такой подход в проектах.
**ВАЖНО! Не забудьте добавить аннотацию `@Validated` к классу, этот параметр позволяет Spring'у проверять параметры методов.**
```java
@Service
@Validated
public class PersonService {
public void validatePersonRequest(@Valid PersonRequest personRequest) {
// выполнение действий
}
}
```
**Тестирование:**
```java
@RunWith(SpringRunner.class)
@SpringBootTest
public class PersonServiceTest {
@Autowired
private PersonService service;
@Test
public void should_throw_exception_when_person_request_is_not_valid() {
try {
PersonRequest personRequest = PersonRequest.builder().sex("Man22")
.classId("82938390").build();
service.validatePersonRequest(personRequest);
} catch (ConstraintViolationException e) {
// выводим сообщение об ошибке
e.getConstraintViolations().forEach(constraintViolation -> System.out.println(constraintViolation.getMessage()));
}
}
}
```
Результат вывода:
```
name не может быть пустым
sex значение не входит в диапазон допустимых значений
```
## Программный подход к валидации параметров
В некоторых случаях может потребоваться ручная проверка параметров и получение результатов валидации.Мы можем получить пример валидатора через фабрику `Validator`. Если вы работаете с Spring Bean, вы также можете использовать аннотацию `@Autowired` для внедрения валидатора.```java
@Autowired
Validator validator
```
Пример использования:
```java
/**
* Ручная проверка объекта
*/
@Test
public void check_person_manually() {
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
PersonRequest personRequest = PersonRequest.builder().sex("Man22")
.classId("82938390").build();
Set<ConstraintViolation<PersonRequest>> violations = validator.validate(personRequest);
violations.forEach(constraintViolation -> System.out.println(constraintViolation.getMessage()));
}
```
Результат вывода:
```
sex значение не входит в диапазон допустимых значений
name не может быть пустым
```
## Создание пользовательских валидаторов (практически полезно)
Если встроенные аннотации валидации не удовлетворяют ваши требования, вы можете создать свои собственные.
### Пример 1: Валидация значения определенного поля в диапазоне допустимых значений
Пример добавления нового требования: полю `Region` в классе `PersonRequest` можно присвоить только одно из трех значений: `China`, `China-Taiwan`, `China-HongKong`.
**Шаг 1: Создайте аннотацию `Region`.**
```java
@Target({FIELD})
@Retention(RUNTIME)
@Constraint(validatedBy = RegionValidator.class)
@Documented
public @interface Region {
String message() default "Значение Region не входит в допустимый диапазон";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
```
**Шаг 2: Реализуйте интерфейс `ConstraintValidator` и переопределите метод `isValid`.**
```java
public class RegionValidator implements ConstraintValidator<Region, String> {
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
HashSet<String> regions = new HashSet<>();
regions.add("China");
regions.add("China-Taiwan");
regions.add("China-HongKong");
return regions.contains(value);
}
}
```Теперь вы можете использовать эту аннотацию:
```java
@Region
private String region;
```
**Тестирование**
```java
PersonRequest personRequest = PersonRequest.builder()
.region("Shanghai").build();
mockMvc.perform(post("/api/person")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(personRequest)))
.andExpect(MockMvcResultMatchers.jsonPath("region").value("Значение region не входит в допустимый диапазон"));
```
**Проверка с помощью Postman**

### Пример 2: Валидация номера телефона
Проверка корректности номера телефона с помощью регулярных выражений. Вы можете найти подходящие регулярные выражения для различных операторов на веб-сайте.
`PhoneNumber.java`
```java
@Documented
@Constraint(validatedBy = PhoneNumberValidator.class)
@Target({FIELD, PARAMETER})
@Retention(RUNTIME)
public @interface PhoneNumber {
String message() default "Некорректный номер телефона";
Class<?>[] groups() default {};
Class<?>[] payload() default {};
}
```
`PhoneNumberValidator.java`
```java
public class PhoneNumberValidator implements ConstraintValidator<PhoneNumber, String> {
@Override
public boolean isValid(String phoneField, ConstraintValidatorContext context) {
if (phoneField == null) {
// может быть null
return true;
}
// 大陆手机号码11位数,匹配格式:前三位固定格式+后8位任意数
// ^ 匹配输入字符串开始的位置
// \d 匹配一个或多个数字,其中 \ 要转义,所以是 \\d
// $ 匹配输入字符串结尾的位置
String regExp = "^[1]((3[0-9])|(4[5-9])|(5[0-3,5-9])|([6][5,6])|(7[0-9])|(8[0-9])|(9[1,8,9]))\\d{8}$";
return phoneField.matches(regExp);
}
}
```
Перевод завершен, теперь мы можем использовать этот аннотацию.```java
@PhoneNumber(message = "некорректный формат номера телефона")
@NotNull(message = "номер телефона не может быть пустым")
private String phoneNumber;
```
**Тестирование**
```java
PersonRequest personRequest = PersonRequest.builder()
.phoneNumber("1816313815").build();
mockMvc.perform(post("/api/person")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(personRequest)))
.andExpect(MockMvcResultMatchers.jsonPath("phoneNumber").value("некорректный формат номера телефона"));
```

## Использование групп для валидации
Группы для валидации мы редко используем, и не рекомендуем использовать их в проектах, так как они сложны для понимания и реализации. Достаточно знать основные моменты!
Когда у нас есть разные методы для работы с объектом и для них требуются разные правила валидации, мы можем использовать группы для валидации.
Я напишу простой пример, чтобы вы могли лучше понять!
**1. Создаем два интерфейса, которые будут представлять разные группы валидации**
```java
public interface AddPersonGroup {
}
public interface DeletePersonGroup {
}
```
**2. Использование групп для валидации**
```java
@Data
public class Person {
// Когда группа валидации - DeletePersonGroup, поле group не должно быть пустым
@NotNull(groups = DeletePersonGroup.class)
// Когда группа валидации - AddPersonGroup, поле group должно быть пустым
@Null(groups = AddPersonGroup.class)
private String group;
}
@Service
@Validated
public class PersonService {
@Validated(AddPersonGroup.class)
public void validatePersonGroupForAdd(@Valid Person person) {
// делаем что-то
}
```
``````md
@Validated(DeletePersonGroup.class)
public void validatePersonGroupForDelete(@Valid Person person) {
// выполняет какое-то действие
}}
```
Проверка через тесты:
```java
@Test(expected = ConstraintViolationException.class)
public void should_check_person_with_groups() {
Person person = new Person();
person.setGroup("group1");
service.validatePersonGroupForAdd(person);
}
@Test(expected = ConstraintViolationException.class)
public void should_check_person_with_groups2() {
Person person = new Person();
service.validatePersonGroupForDelete(person);
}
```
Использование групп для проверки создает ощущение, что это противоположно лучшим практикам и ухудшает поддерживаемость кода. Лучше избегать этого!
## Обзор часто используемых аннотаций для проверки
`JSR303` определяет стандарт `validation-api` для `Bean Validation` (проверки), но не предоставляет реализацию. `Hibernate Validation` является реализацией этого стандарта `hibernate-validator` и добавляет дополнительные аннотации, такие как `@Email`, `@Length`, `@Range` и другие. `Spring Validation` использует `Hibernate Validation` в качестве основы.
**Аннотации, предоставляемые JSR**:
- `@Null` — проверяет, что значение равно `null`.
- `@NotNull` — проверяет, что значение не равно `null`.
- `@Size` — проверяет длину строки, коллекции или массива.
- `@Digits` — проверяет, что строка представляет собой число с определенным количеством цифр до и после запятой.
- `@Past` — проверяет, что значение является датой в прошлом.
- `@Future` — проверяет, что значение является датой в будущем.
- `@Pattern` — проверяет, что строка соответствует определенному регулярному выражению.
- `@Min` — проверяет, что значение больше или равно указанному числу.
- `@Max` — проверяет, что значение меньше или равно указанному числу.
- `@DecimalMin` — проверяет, что значение больше или равно указанному десятичному числу.
- `@DecimalMax` — проверяет, что значение меньше или равно указанному десятичному числу.
- `@Digits` — проверяет, что строка представляет собой число с определенным колич�数字转换错误,正确的翻译应该是:
- `@Null` — проверяет, что значение равно `null`.
- `@NotNull` — проверяет, что значение не равно `null`.
- `@Size` — проверяет длину строки, коллекции или массива.
- `@Digits` — проверяет, что строка представляет собой число с определенным количеством цифр до и после запятой.
- `@Past` — проверяет, что значение является датой в прошлом.
- `@Future` — проверяет, что значение является датой в будущем.
- `@Pattern` — проверяет, что строка соответствует определенному регулярному выражению.
- `@Min` — проверяет, что значение больше или равно указанному числу.
- `@Max` — проверяет, что значение меньше или равно указанному числу.
- `@DecimalMin` — проверяет, что значение больше или равно указанному десятичному числу.
- `@DecimalMax` — проверяет, что значение меньше или равно указанному десятичному числу.
- `@Digits` — проверяет, что строка представляет собой число с определенным количеством цифр до и после запятой.- `@Null` Элемент, помеченный этой аннотацией, должен быть null.
- `@NotNull` Элемент, помеченный этой аннотацией, не должен быть null.
- `@AssertTrue` Элемент, помеченный этой аннотацией, должен быть true.
- `@AssertFalse` Элемент, помеченный этой аннотацией, должен быть false.
- `@Min(value)` Элемент, помеченный этой аннотацией, должен быть числом, значение которого должно быть больше или равно указанному минимальному значению.
- `@Max(value)` Элемент, помеченный этой аннотацией, должен быть числом, значение которого должно быть меньше или равно указанному максимальному значению.
- `@DecimalMin(value)` Элемент, помеченный этой аннотацией, должен быть числом, значение которого должно быть больше или равно указанному минимальному значению.
- `@DecimalMax(value)` Элемент, помеченный этой аннотацией, должен быть числом, значение которого должно быть меньше или равно указанному максимальному значению.
- `@Size(max=, min=)` Элемент, помеченный этой аннотацией, должен быть строкой, длина которой должна быть в пределах указанного диапазона.
- `@Digits(integer, fraction)` Элемент, помеченный этой аннотацией, должен быть числом, значение которого должно быть в пределах допустимого диапазона.
- `@Past` Элемент, помеченный этой аннотацией, должен быть датой, которая находится в прошлом.
- `@Future` Элемент, помеченный этой аннотацией, должен быть датой, которая находится в будущем.- `@Pattern(regex=,flag=)` Элемент, помеченный этой аннотацией, должен соответствовать указанному регулярному выражению.**Аннотации, предоставляемые Hibernate Validator**:
- `@NotBlank(message =)` Элемент, помеченный этой аннотацией, должен быть строкой, длина которой должна быть больше нуля.
- `@Email` Элемент, помеченный этой аннотацией, должен быть адресом электронной почты.
- `@Length(min =, max =)` Элемент, помеченный этой аннотацией, должен быть строкой, длина которой должна быть в пределах указанного диапазона.
- `@NotEmpty` Элемент, помеченный этой аннотацией, должен быть строкой, длина которой должна быть больше нуля.
- `@Range(min =, max =, message =)` Элемент, помеченный этой аннотацией, должен быть числом, значение которого должно быть в пределах указанного диапазона.
```## Расширение
Часто мне задают вопрос: "Какая разница между `@NotNull` и `@Column(nullable = false)`?"
Отвечаю кратко:
- `@NotNull` — это аннотация для валидации бинов JSR 303, и она не связана напрямую с ограничениями базы данных.
- `@Column(nullable = false)` — это способ указания JPA, что колонка должна быть неотъемлемой при создании таблицы.
Вкратце, первое используется для валидации, а второе — для указания ограничений таблицы при её создании в базе данных.
```
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Опубликовать ( 0 )