Skip to content

Instantly share code, notes, and snippets.

@mloza

mloza/Main.java Secret

Last active November 15, 2020 14:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mloza/870ce1463966b6b8134544d09fb3b051 to your computer and use it in GitHub Desktop.
Save mloza/870ce1463966b6b8134544d09fb3b051 to your computer and use it in GitHub Desktop.
Kod źródłowy do wpisu o Java Bean Validation w Spring Boot znajdującego się pod adresem: https://blog.mloza.pl/
@SpringBootApplication
public class Main {
public static void main(String[] args) {
SpringApplication.run(Main.class);
}
}
<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>
@PostMapping("/data")
@ResponseBody
public String validationBody(
@RequestBody @Valid PostBody body // #1
) {
return "OK! Everything is valid";
}
public class PostBody {
@Email
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