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)
@RequiredArgsConstructor @Getter
public class ShipmentComponentDTO {
private final String productCode;
}
Enter fullscreen mode Exit fullscreen mode
@RequiredArgsConstructor @Getter
public class ShipmentDTO {
private final String productCode;
private final int count;
private final List<ShipmentComponentDTO> shipmentComponentDTOs;
}
Enter fullscreen mode Exit fullscreen mode
Now, the controller
@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);
}
}
Enter fullscreen mode Exit fullscreen mode
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
@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 = "";
}
}
Enter fullscreen mode Exit fullscreen mode
@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;
}
Enter fullscreen mode Exit fullscreen mode
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
@RestController
public class ShipmentController_Validation {
@PostMapping("validation/shipment/add")
public ResponseEntity<String> addShipment(@RequestBody @Validated ShipmentDTO shipmentDTO){
return ResponseEntity.ok(":)");
}
}
Enter fullscreen mode Exit fullscreen mode
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
@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);
}
}
Enter fullscreen mode Exit fullscreen mode
In conclusion️
- Imagine having an API with many endpoints that we need to validate. Which method would be better?
- The Validator.
暂无评论内容