Kod źródłowy do wpisu o Java Bean Validation w Spring Boot znajdującego się pod adresem: https://blog.mloza.pl/
<dependency> | |
<groupId>org.springframework.boot</groupId> | |
<artifactId>spring-boot-starter-data-jpa</artifactId> | |
<version>2.3.2.RELEASE</version> | |
</dependency> | |
<dependency> | |
<groupId>com.h2database</groupId> | |
<artifactId>h2</artifactId> | |
<version>1.4.199</version> | |
</dependency> |
<?xml version="1.0" encoding="UTF-8"?> | |
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |
<modelVersion>4.0.0</modelVersion> | |
<groupId>pl.mloza</groupId> | |
<artifactId>bean-validation</artifactId> | |
<version>1.0-SNAPSHOT</version> | |
<dependencies> | |
<dependency> | |
<groupId>org.springframework.boot</groupId> | |
<artifactId>spring-boot-starter-web</artifactId> | |
<version>2.3.2.RELEASE</version> | |
</dependency> | |
<dependency> | |
<groupId>javax.validation</groupId> | |
<artifactId>validation-api</artifactId> | |
<version>2.0.0.Final</version> | |
</dependency> | |
<dependency> | |
<groupId>org.hibernate.validator</groupId> | |
<artifactId>hibernate-validator</artifactId> | |
<version>6.1.5.Final</version> | |
</dependency> | |
<dependency> | |
<groupId>org.springframework.boot</groupId> | |
<artifactId>spring-boot-starter-test</artifactId> | |
<version>2.3.2.RELEASE</version> | |
<scope>test</scope> | |
</dependency> | |
</dependencies> | |
<build> | |
<plugins> | |
<plugin> | |
<groupId>org.apache.maven.plugins</groupId> | |
<artifactId>maven-compiler-plugin</artifactId> | |
<version>3.8.1</version> | |
<configuration> | |
<release>14</release> | |
</configuration> | |
</plugin> | |
</plugins> | |
</build> | |
</project> |
public class PostBody { | |
private String email; | |
@Min(value = 18, message = "You're too young") | |
private int age; | |
@Size(min = 2, max = 10) | |
private String name; | |
@Pattern(regexp = "^[0-9]{2}-[0-9]{3}$") | |
private String zipCode; | |
} |
@Controller | |
@Validated // #1 | |
public class ValidateController { | |
@GetMapping("/site/{id}") | |
@ResponseBody | |
public String validationInPath( | |
@PathVariable("id") @Min(10) @Max(20) int id // #2 | |
) { | |
return "OK! id: " + id; | |
} | |
@GetMapping("/query") | |
@ResponseBody | |
public String validationInQuery( | |
@RequestParam("id") @Min(10) int id // #3 | |
) { | |
return "OK! id: " + id; | |
} | |
} |
@WebMvcTest | |
public class ValidateControllerTest { | |
@Autowired | |
private MockMvc mvc; | |
@Autowired | |
private ObjectMapper objectMapper; | |
@Test | |
public void shouldReturnErrorWhenInputIsInvalid() throws Exception { | |
mvc.perform(post("/data") | |
.contentType(MediaType.APPLICATION_JSON) | |
.content(invalidBody())) | |
.andDo(mvcResult -> System.out.println(mvcResult.getResponse().getContentAsString())) | |
.andExpect(status().isBadRequest()); | |
} | |
private String invalidBody() throws JsonProcessingException { | |
return objectMapper.writeValueAsString( | |
new PostBody( | |
"not-valid-email", | |
2, | |
"Very long name that is beyond constraint", | |
"10-1000")); | |
} | |
} |
@ControllerAdvice // #1 | |
public class ValidationErrorControllerAdvice { | |
@ExceptionHandler(ConstraintViolationException.class) // #2 | |
@ResponseStatus(HttpStatus.BAD_REQUEST) // #3 | |
@ResponseBody | |
public ValidationErrorResponse validationError(ConstraintViolationException exception) { | |
ValidationErrorResponse response = new ValidationErrorResponse(); | |
for(ConstraintViolation error: exception.getConstraintViolations()) { | |
response.addError( | |
new ValidationError( | |
error.getPropertyPath().toString(), | |
error.getMessage())); | |
} | |
return response; | |
} | |
@ExceptionHandler(MethodArgumentNotValidException.class) | |
@ResponseStatus(HttpStatus.BAD_REQUEST) | |
@ResponseBody | |
public ValidationErrorResponse validationError(MethodArgumentNotValidException exception) { | |
ValidationErrorResponse response = new ValidationErrorResponse(); | |
for(FieldError error: exception.getBindingResult().getFieldErrors()) { | |
response.addError( | |
new ValidationError( | |
error.getField(), | |
error.getDefaultMessage())); | |
} | |
return response; | |
} | |
} |
public class ValidationErrorResponse { | |
private List<ValidationError> errors = new ArrayList<>(); | |
// setters, getters | |
public ValidationErrorResponse addError(ValidationError error) { | |
this.errors.add(error); | |
return this; | |
} | |
} | |
public class ValidationError { | |
private String fieldName; | |
private String message; | |
public ValidationError(String fieldName, String message) { | |
this.fieldName = fieldName; | |
this.message = message; | |
} | |
public ValidationError() { | |
} | |
// Setters, getters | |
} |
@Validated | |
@Service | |
public class ValidationInService { | |
void validatePostBody(@Valid PostBody postBody) { | |
System.out.println("PostBody is valid!"); | |
} | |
} |
@SpringBootTest | |
public class ValidationInServiceTest { | |
@Autowired | |
private ValidationInService validationInService; | |
@Test | |
public void shouldValidateDataPassedToService() { | |
assertThrows( | |
ConstraintViolationException.class, | |
() -> validationInService.validatePostBody(invalidBody())); | |
} | |
private PostBody invalidBody() { | |
return new PostBody( | |
"not-valid-email", | |
2, | |
"Very long name that is beyond constraint", | |
"10-1000"); | |
} | |
} |
@Entity | |
public class ValidEntity { | |
@Id | |
@GeneratedValue | |
private Long id; | |
@Column | |
@Min(18) | |
@Max(130) | |
private Integer age; | |
@Size(min = 2, max=128) | |
@Column | |
private String name; | |
@Column | |
@Pattern(regexp = "[0-9]{2}-[0-9]{3}") | |
private String zipCode; | |
// getters, setters etc... | |
} |
@SpringBootTest | |
public class ValidEntityTest { | |
@Autowired | |
private ValidEntityRepository validEntityRepository; | |
@Test | |
public void shouldThrowErrorOnInvalidEntity() { | |
ValidEntity invalidEntity = new ValidEntity(10, "O", "123-123"); | |
assertThrows( | |
TransactionSystemException.class, | |
() -> validEntityRepository.save(invalidEntity)); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment