Compare commits

...

2 Commits

Author SHA1 Message Date
9d44c12fac
Postman 2024-11-04 12:17:41 +03:00
95a6621d6c
Обновление зависимостей 2024-11-04 11:55:44 +03:00
13 changed files with 241 additions and 33 deletions

15
pom.xml
View File

@ -2,10 +2,11 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-parent -->
<parent> <parent>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId> <artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.1</version> <version>3.3.5</version>
<relativePath/> <!-- lookup parent from repository --> <relativePath/> <!-- lookup parent from repository -->
</parent> </parent>
@ -15,7 +16,7 @@
<name>spring-validation</name> <name>spring-validation</name>
<properties> <properties>
<java.version>17</java.version> <java.version>21</java.version>
<maven.compiler.source>${java.version}</maven.compiler.source> <maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target> <maven.compiler.target>${java.version}</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
@ -28,15 +29,11 @@
<artifactId>spring-boot-starter-validation</artifactId> <artifactId>spring-boot-starter-validation</artifactId>
</dependency> </dependency>
<dependency> <!-- https://mvnrepository.com/artifact/org.springdoc/springdoc-openapi-starter-webmvc-ui -->
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-annotations</artifactId>
<version>2.2.1</version>
</dependency>
<dependency> <dependency>
<groupId>org.springdoc</groupId> <groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId> <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>1.6.9</version> <version>2.6.0</version>
</dependency> </dependency>
<dependency> <dependency>

View File

@ -0,0 +1,109 @@
{
"info": {
"_postman_id": "bb233500-0318-496e-9882-071ee5de34a0",
"name": "Spring Validation",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
"_exporter_id": "16442716"
},
"item": [
{
"name": "Person",
"item": [
{
"name": "Create Person",
"event": [
{
"listen": "test",
"script": {
"exec": [
"pm.test(\"Status code is 400\", function () {",
" pm.response.to.have.status(400);",
"});",
"",
"pm.test(\"violations is not null\", function () {",
" var jsonData = pm.response.json();",
" pm.expect(jsonData.violations).is.not.null;",
" pm.expect(jsonData.violations[0].fieldName).to.be.oneOf([\"numberBetweenOneAndTen\", \"ipAddress\", \"name\"])",
" pm.expect(jsonData.violations[1].fieldName).to.be.oneOf([\"numberBetweenOneAndTen\", \"ipAddress\", \"name\"])",
" pm.expect(jsonData.violations[2].fieldName).to.be.oneOf([\"numberBetweenOneAndTen\", \"ipAddress\", \"name\"])",
" pm.expect(jsonData.violations[0].message).to.be.oneOf([\"Не соответствует формату IP адреса\", \"должно быть не больше 10\", \"не должно быть пустым\"])",
" pm.expect(jsonData.violations[1].message).to.be.oneOf([\"Не соответствует формату IP адреса\", \"должно быть не больше 10\", \"не должно быть пустым\"])",
" pm.expect(jsonData.violations[2].message).to.be.oneOf([\"Не соответствует формату IP адреса\", \"должно быть не больше 10\", \"не должно быть пустым\"])",
"});"
],
"type": "text/javascript"
}
}
],
"request": {
"method": "POST",
"header": [],
"body": {
"mode": "raw",
"raw": "{\n \"name\": null,\n \"numberBetweenOneAndTen\": 20,\n \"ipAddress\": \"15434.250.124.\"\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "http://localhost:8080/api/person",
"protocol": "http",
"host": [
"localhost"
],
"port": "8080",
"path": [
"api",
"person"
]
}
},
"response": []
},
{
"name": "Get Person",
"event": [
{
"listen": "test",
"script": {
"exec": [
"pm.test(\"Status code is 400\", function () {",
" pm.response.to.have.status(400);",
"});",
"",
"pm.test(\"violations is not null\", function () {",
" var jsonData = pm.response.json();",
" pm.expect(jsonData.violations).is.not.null;",
" pm.expect(jsonData.violations[0].fieldName).to.be.eq(\"getById.personId\")",
" pm.expect(jsonData.violations[0].message).to.be.eq(\"должно быть не меньше 0\")",
"});"
],
"type": "text/javascript"
}
}
],
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "http://localhost:8080/api/person/-1",
"protocol": "http",
"host": [
"localhost"
],
"port": "8080",
"path": [
"api",
"person",
"-1"
]
}
},
"response": []
}
]
}
]
}

View File

@ -1,5 +1,6 @@
package org.sadtech.example.springvalidation.controller; package org.sadtech.example.springvalidation.controller;
import jakarta.validation.ConstraintViolationException;
import org.sadtech.example.springvalidation.dto.ValidationErrorResponse; import org.sadtech.example.springvalidation.dto.ValidationErrorResponse;
import org.sadtech.example.springvalidation.dto.Violation; import org.sadtech.example.springvalidation.dto.Violation;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
@ -9,7 +10,6 @@ import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.ResponseStatus;
import javax.validation.ConstraintViolationException;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;

View File

@ -2,7 +2,12 @@ package org.sadtech.example.springvalidation.controller;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import lombok.RequiredArgsConstructor;
import org.sadtech.example.springvalidation.dto.PersonDto; import org.sadtech.example.springvalidation.dto.PersonDto;
import org.sadtech.example.springvalidation.service.PersonService;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
@ -13,16 +18,18 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid; import java.time.LocalDate;
import javax.validation.constraints.Min; import java.util.List;
import javax.validation.constraints.NotBlank;
@Validated @Validated
@RestController @RestController
@RequiredArgsConstructor
@RequestMapping("/api/person") @RequestMapping("/api/person")
@Tag(name = "Пользователи системы", description = "Валидация на уровне контроллера") @Tag(name = "Пользователи системы", description = "Валидация на уровне контроллера")
public class PersonController { public class PersonController {
private final PersonService personService;
@PostMapping @PostMapping
@Operation(summary = "Сохранение пользователя") @Operation(summary = "Сохранение пользователя")
public ResponseEntity<String> save(@Valid @RequestBody PersonDto personDto) { public ResponseEntity<String> save(@Valid @RequestBody PersonDto personDto) {
@ -45,4 +52,15 @@ public class PersonController {
return ResponseEntity.ok("valid"); return ResponseEntity.ok("valid");
} }
@GetMapping("birthday")
@Operation(summary = "Получить пользователей в диапазоне ДР")
public ResponseEntity<List<PersonDto>> getByPersonBirthday(
@RequestParam("from") LocalDate from,
@RequestParam("to") LocalDate to
) {
return ResponseEntity.ok(
personService.getAllByHappyBirthdayBetween(from, to)
);
}
} }

View File

@ -1,6 +1,7 @@
package org.sadtech.example.springvalidation.controller; package org.sadtech.example.springvalidation.controller;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import org.sadtech.example.springvalidation.dto.PersonDto; import org.sadtech.example.springvalidation.dto.PersonDto;
import org.sadtech.example.springvalidation.valid.Marker; import org.sadtech.example.springvalidation.valid.Marker;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
@ -11,7 +12,6 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
@Validated @Validated
@RestController @RestController

View File

@ -12,9 +12,9 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
@RestController @RestController
@RequiredArgsConstructor
@RequestMapping("/api/no-valid/person") @RequestMapping("/api/no-valid/person")
@Tag(name = "Пользователи системы 2", description = "Валидация на уровне сервиса") @Tag(name = "Пользователи системы 2", description = "Валидация на уровне сервиса")
@RequiredArgsConstructor
public class PersonControllerNoValidation { public class PersonControllerNoValidation {
private final PersonService personService; private final PersonService personService;

View File

@ -0,0 +1,18 @@
package org.sadtech.example.springvalidation.dto;
import jakarta.validation.constraints.NotBlank;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class Address {
@NotBlank
private String state;
@NotBlank
private String city;
}

View File

@ -1,23 +1,23 @@
package org.sadtech.example.springvalidation.dto; package org.sadtech.example.springvalidation.dto;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import jakarta.persistence.Transient;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Null;
import jakarta.validation.constraints.Pattern;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import org.sadtech.example.springvalidation.valid.CapitalLetter; import org.sadtech.example.springvalidation.valid.CapitalLetter;
import org.sadtech.example.springvalidation.valid.Marker; import org.sadtech.example.springvalidation.valid.Marker;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Null;
import javax.validation.constraints.Pattern;
@Entity @Entity
@Getter @Getter
@Setter @Setter
@ -46,4 +46,7 @@ public class PersonDto {
) )
private String ipAddress; private String ipAddress;
@Transient
private Address address;
} }

View File

@ -1,10 +1,14 @@
package org.sadtech.example.springvalidation.service; package org.sadtech.example.springvalidation.service;
import jakarta.validation.Valid;
import org.sadtech.example.springvalidation.dto.PersonDto; import org.sadtech.example.springvalidation.dto.PersonDto;
import org.sadtech.example.springvalidation.valid.LocalDateBetween;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import javax.validation.Valid; import java.time.LocalDate;
import java.util.Collections;
import java.util.List;
@Service @Service
@Validated @Validated
@ -14,4 +18,10 @@ public class PersonService {
// do something // do something
} }
@LocalDateBetween
public List<PersonDto> getAllByHappyBirthdayBetween(LocalDate from, LocalDate to) {
System.out.println("valid");
return Collections.emptyList();
}
} }

View File

@ -1,7 +1,8 @@
package org.sadtech.example.springvalidation.valid; package org.sadtech.example.springvalidation.valid;
import javax.validation.Constraint; import jakarta.validation.Constraint;
import javax.validation.Payload; import jakarta.validation.Payload;
import java.lang.annotation.Documented; import java.lang.annotation.Documented;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.Target; import java.lang.annotation.Target;

View File

@ -1,7 +1,8 @@
package org.sadtech.example.springvalidation.valid; package org.sadtech.example.springvalidation.valid;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext; import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
public class CapitalLetterValidator implements ConstraintValidator<CapitalLetter, String> { public class CapitalLetterValidator implements ConstraintValidator<CapitalLetter, String> {

View File

@ -0,0 +1,23 @@
package org.sadtech.example.springvalidation.valid;
import jakarta.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Documented
@Target(METHOD)
@Retention(RUNTIME)
public @interface LocalDateBetween {
String message() default "Дата невалидна";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}

View File

@ -0,0 +1,28 @@
package org.sadtech.example.springvalidation.valid;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import jakarta.validation.constraintvalidation.SupportedValidationTarget;
import jakarta.validation.constraintvalidation.ValidationTarget;
import java.time.LocalDate;
@SupportedValidationTarget(ValidationTarget.PARAMETERS)
public class LocalDateBetweenValidator implements ConstraintValidator<LocalDateBetween, Object[]> {
@Override
public boolean isValid(Object[] value, ConstraintValidatorContext context) {
final Object objDateFrom = value[0];
final Object objDateTo = value[1];
if (
!(objDateFrom instanceof final LocalDate dateFrom)
|| !(objDateTo instanceof final LocalDate dateTo)
) {
throw new IllegalArgumentException("Illegal method signature, expected two parameters of type LocalDate.");
}
return dateFrom.isAfter(LocalDate.now()) && dateFrom.isBefore(dateTo);
}
}