Validate Your Endpoints Using Spring
Tags: webdev, java, tutorial, programming
Published: 2021-06-11 21:16:19

## **What Do We Want To Do?** 🤔
Apply validation to the objects we receive at the endpoint controllers from clients.

## **What Is The First Solution That Comes To Mind?** 🧠
it's simple, right? write functions that validate those objects.

Say we have an endpoint to add new Shipments. 🚚

Let's start with the DTO classes (Data Transfer Object)
```java
@RequiredArgsConstructor @Getter  
public class ShipmentComponentDTO {  
    private final String productCode;  
}
```
```java
@RequiredArgsConstructor @Getter  
public class ShipmentDTO {  
    private final String productCode;  
    private final int count;  
    private final List<ShipmentComponentDTO> shipmentComponentDTOs;  
}
```
Now, the controller
```java
@RestController  
public class ShipmentController {  
    @PostMapping("/shipment/add")  
    public ResponseEntity<String> addShipment(@RequestBody ShipmentDTO shipmentDTO){  
        if (shipmentDTO.getProductCode() == null)  
            return ResponseEntity.badRequest().body("Product code can't be null");  
  
        if (shipmentDTO.getCount() <= 0)  
            return ResponseEntity.badRequest().body("Count can't be negative or zero");  
  
        if (!allComponentsAreValid(shipmentDTO))  
            return ResponseEntity.badRequest().body("Component product code can't be null");  
  
        return ResponseEntity.ok(":)");  
    }  
  
    private boolean allComponentsAreValid(ShipmentDTO shipmentDTO) {  
        List<ShipmentComponentDTO> shipmentComponentDTOs = shipmentDTO.getShipmentComponentDTOs();  
  
        return shipmentComponentDTOs != null &&  
                shipmentComponentDTOs.size() > 0 &&  
                shipmentComponentDTOs.  
                stream().  
                allMatch(shipmentComponentDTO -> shipmentComponentDTO.getProductCode() != null);  
    }  
}
```
#### Notice all the manual tedious work we had to do.🤕
#### Can't anyone help us make this task more fun?😏

## Please meet the Javax validator.🥳
- It provides a set of annotations that are used with class fields.
- They act as constraints.
	- this field should not be null
	- this list should have a minimum size of 5
	- and many more...

## How to do it?👀

For the object we want to validate
- We annotate the fields we want to validate with annotations from ```javax.validation.constraints```

- Then, we annotate the parameter of the controller where we want to validate.

- We then have to catch the error thrown by the validator, and return a proper response to the user.

### Let's code it 🦾
Here are the DTO objects
```java
@RequiredArgsConstructor @Getter  
public class ShipmentComponentDTO {  
    @NotBlank(message = "Component product code can't be null")  
    private final String productCode;  
    // required by the javax validation code  
  public ShipmentComponentDTO() {  
        this.productCode = "";  
    }  
}
```
```java
@RequiredArgsConstructor @Getter  
public class ShipmentDTO {  
    @NotBlank(message = "Product code can't be null")  
    private final String productCode;  
    @Min(1)  
    private final int count;  
  
    @Size(min = 1, message = "Component product code can't be empty")  
    @NotNull(message = "Component product code can't be null")  
    @Valid  
  private final List<ShipmentComponentDTO> shipmentComponentDTOs;  
}
```
### Ok, what were those annotations?👇
- NotBlank
	- This field can't be null or empty string
- Min
	- this integer should have a min value we specify
- Size
	- this array's size should be within the boundaries we specify
- NotNull
	- This field must not be null 
- Valid
	- Validate the fields in this object 

### Now, here is the controller👇
```java
@RestController  
public class ShipmentController_Validation {  
    @PostMapping("validation/shipment/add")  
    public ResponseEntity<String> addShipment(@RequestBody @Validated ShipmentDTO shipmentDTO){  
        return ResponseEntity.ok(":)");  
    }  
}
```
### What was that @Validated annotation?🤨
- it tells Spring to run the validation mechanism on this object according to its validation annotations(NotBlank, etc..)

### Finally, let's catch the exception thrown by the validator and generate a proper response
```java
@Order(Ordered.HIGHEST_PRECEDENCE)  
@ControllerAdvice  
public class ValidationAdvice{  
    @ResponseStatus(BAD_REQUEST)  
    @ResponseBody  
 @ExceptionHandler(MethodArgumentNotValidException.class)  
    public ResponseEntity<?> methodArgumentNotValidException(MethodArgumentNotValidException ex) {  
        BindingResult result = ex.getBindingResult();  
        List<FieldError> fieldErrors = result.getFieldErrors();  
        String errorMessage = fieldErrors.get(0).getDefaultMessage();  
        return ResponseEntity.badRequest().body(errorMessage);  
    }  
}
```
## In conclusion✍️
- Imagine having an API with many endpoints that we need to validate. Which method would be better?
- The Validator.

## Code on [GitHub] (https://github.com/Jarjanazy/Spring-Validation-demo)