Validate Your Endpoints Using Spring
Abdulcelil Cercenazi
Posted on June 11, 2021
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;
}
@RequiredArgsConstructor @Getter
public class ShipmentDTO {
private final String productCode;
private final int count;
private final List<ShipmentComponentDTO> shipmentComponentDTOs;
}
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);
}
}
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 = "";
}
}
@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👇
@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
@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
💖 💪 🙅 🚩
Abdulcelil Cercenazi
Posted on June 11, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
webdev Getting Back to Java: A Journey Through One of the Most Versatile Languages
November 27, 2024
webdev Mastering the Factory Method Design Pattern: Building a Task Management CLI
November 14, 2024
webdev How to Create an Advanced "Show Password" Feature Using HTML, CSS, and JavaScript
October 21, 2024