@Valid + Jakarta with DTO in Spring Boot 3

wkreuch

William

Posted on January 25, 2024

@Valid + Jakarta with DTO in Spring Boot 3

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>
Enter fullscreen mode Exit fullscreen mode

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) {
}
Enter fullscreen mode Exit fullscreen mode

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));
    }
[...]
}
Enter fullscreen mode Exit fullscreen mode

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);
    }
Enter fullscreen mode Exit fullscreen mode

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"
}
Enter fullscreen mode Exit fullscreen mode

post

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.

💖 💪 🙅 🚩
wkreuch
William

Posted on January 25, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related