@Valid + Jakarta with DTO in Spring Boot 3
Tags: java, springboot, spring, restapi
Published: 2024-01-25 03:28:42

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](https://jakarta.ee/specifications/bean-validation/3.0/apidocs/jakarta/validation/constraints/package-summary).

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"
}
```

![post](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oh1md19gu57yp9e7e84m.png)

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.