@Valid + Jakarta with DTO in Spring Boot 3
William
Posted on January 25, 2024
Every API REST need validations, and to resolve the initial situations, can be use jakarta to help us to do it fast and don't necessarily create a IF for each atribute.
Add dependency
In the pom.xml put this new dependency to use jakarta.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
Implement validations in DTO
In the DTO class, in this case only in record ProductCreateUpdate, put the annotations from jakarta before each variable.
package com.spring.models.dto;
import jakarta.validation.constraints.*;
public record ProductCreateUpdate(@NotBlank @Size(min = 2, max = 125) String name, @PositiveOrZero Double price, @Size(max = 500) String description) {
}
The annotations is not difficult understand, the name of each means what to do, it's possible use a combination in only one. You can figure out all annotations in this documentation.
For this validation to be used when receiving the body, it's necessary to use the @Valid annotation in the controller method declaration.
import jakarta.validation.Valid;
public class ProductController {
[...]
@PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Product> create(@RequestBody @Valid ProductCreateUpdate productCreate) {
Product product = new Product();
BeanUtils.copyProperties(productCreate, product);
return ResponseEntity.status(HttpStatus.CREATED).body(service.create(product));
}
@PutMapping(path = "/{id}",
consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Product> update(@PathVariable(value = "id") Long id, @RequestBody @Valid ProductCreateUpdate productUpdate) {
Product product = new Product();
BeanUtils.copyProperties(productUpdate, product);
return ResponseEntity.status(HttpStatus.OK).body(service.update(product, id));
}
[...]
}
How the body is of type ProductCreateUpdate, so put @Valid before the body declaration. The validation is necessary only in the POST e PUT request, the validation will work equal for both.
Handle validation exception
To made the response of error look better when the data received is wrong, let's create a method to handle the MethodArgumentNotValidException in CustomizedResponseEntityExceptionHandler.
public class CustomizedResponseEntityExceptionHandler {
[...]
@ExceptionHandler(MethodArgumentNotValidException.class)
public final ResponseEntity<ExceptionResponse> handlerMethodArgumentValidExceptions(MethodArgumentNotValidException exception, WebRequest request) {
var errors = exception.getFieldErrors().stream().map(fieldError -> "Field: " + fieldError.getField() + " Error: " + fieldError.getDefaultMessage()).toList();
ExceptionResponse exceptionResponse = new ExceptionResponse(Instant.now(), errors.toString(), request.getDescription(false));
return new ResponseEntity<>(exceptionResponse, HttpStatus.BAD_REQUEST);
}
The method will take each message error from the list and put everything into a custom message.
Testing
In the postman make this request to create a Product.
POST localhost:8080/api/product
{
"name": "",
"price": -50.00,
"description": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaA"
}
This test is to show all valiton working, look that the response code is 400 Bad Request and in the response, the message is easy to read and understand what is necessary to resolve.
Conclusion
In this post, we create a validation using only annotations by jakarta and that have many options to implement for each situation. Jakarta can be use to valid String, number, date, booleans...
Next Step
In the next step we will use Swagger to create a documentation about the API.
Posted on January 25, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.