Robust RESTful Services with Spring Boot Hands-On Exercises
Exercise 1: Implement Validation in RESTful Services
Objective: Use Hibernate Validator to enforce constraints on your data model and handle validation errors.
Add Validation Dependency:
- Ensure your
pom.xml
includes the validation starter.
- Ensure your
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
Update the
Product
Entity with Validation:- Use validation annotations to enforce constraints.
java
package com.example.demo;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
@Entity
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@NotBlank(message = "Name is mandatory")
private String name;
@Min(value = 0, message = "Price must be non-negative")
private double price;
// Getters and setters
}
Modify the
ProductController
to Handle Validation:- Use
@Valid
to trigger validation on request bodies.
- Use
java
package com.example.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
@RestController
@RequestMapping("/products")
public class ProductController {
@Autowired
private ProductRepository productRepository;
@PostMapping
public ResponseEntity<Product> createProduct(@Valid @RequestBody Product product) {
Product savedProduct = productRepository.save(product);
return ResponseEntity.ok(savedProduct);
}
// Other CRUD methods...
}
Handle Validation Errors:
- Create a global exception handler for
MethodArgumentNotValidException
.
- Create a global exception handler for
java
package com.example.demo;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import java.util.HashMap;
import java.util.Map;
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ResponseEntity<Map<String, String>> handleValidationExceptions(MethodArgumentNotValidException ex) {
Map<String, String> errors = new HashMap<>();
ex.getBindingResult().getAllErrors().forEach((error) -> {
String fieldName = ((FieldError) error).getField();
String errorMessage = error.getDefaultMessage();
errors.put(fieldName, errorMessage);
});
return new ResponseEntity<>(errors, HttpStatus.BAD_REQUEST);
}
// Other exception handlers...
}
Test Validation:
- Use Postman to send requests with invalid data and observe validation error messages.
Exercise 2: Implement Pagination and Sorting
Objective: Enhance the REST API to support pagination and sorting of resources.
Add Pagination and Sorting to Repository:
- Extend
PagingAndSortingRepository
for pagination and sorting capabilities.
- Extend
java
package com.example.demo;
import org.springframework.data.repository.PagingAndSortingRepository;
public interface ProductRepository extends PagingAndSortingRepository<Product, Long> {
}
Update
ProductController
for Pagination and Sorting:- Use
Pageable
to handle pagination and sorting.
- Use
java
package com.example.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/products")
public class ProductController {
@Autowired
private ProductRepository productRepository;
@GetMapping
public Page<Product> getProducts(Pageable pageable) {
return productRepository.findAll(pageable);
}
// Other CRUD methods...
}
Test Pagination and Sorting:
- Use Postman to fetch products with pagination parameters:
http://localhost:8081/products?page=0&size=5&sort=name,asc
.
- Use Postman to fetch products with pagination parameters:
Exercise 3: Implement a Simple Search Functionality
Objective: Add a search feature to filter products by name.
Add a Custom Query Method to
ProductRepository
:- Define a query method using Spring Data JPA conventions.
java
package com.example.demo;
import org.springframework.data.repository.CrudRepository;
import java.util.List;
public interface ProductRepository extends CrudRepository<Product, Long> {
List<Product> findByNameContaining(String name);
}
Add a Search Endpoint in
ProductController
:- Implement a new endpoint to search products by name.
java
package com.example.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/products")
public class ProductController {
@Autowired
private ProductRepository productRepository;
@GetMapping("/search")
public List<Product> searchProducts(@RequestParam String name) {
return productRepository.findByNameContaining(name);
}
// Other CRUD methods...
}
Test the Search Functionality:
- Use Postman to search for products by name:
http://localhost:8081/products/search?name=phone
.
- Use Postman to search for products by name:
These exercises should provide a deeper understanding of building robust RESTful services with Spring Boot.