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.

  1. Add Validation Dependency:

    • Ensure your pom.xml includes the validation starter.

xml

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-validation</artifactId>
    </dependency>
  1. 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
    }
  1. Modify the ProductController to Handle Validation:

    • Use @Valid to trigger validation on request bodies.

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...
    }
  1. Handle Validation Errors:

    • Create a global exception handler for MethodArgumentNotValidException.

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...
    }
  1. 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.

  1. Add Pagination and Sorting to Repository:

    • Extend PagingAndSortingRepository for pagination and sorting capabilities.

java

    package com.example.demo;

    import org.springframework.data.repository.PagingAndSortingRepository;

    public interface ProductRepository extends PagingAndSortingRepository<Product, Long> {
    }
  1. Update ProductController for Pagination and Sorting:

    • Use Pageable to handle pagination and sorting.

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...
    }
  1. Test Pagination and Sorting:


Exercise 3: Implement a Simple Search Functionality

Objective: Add a search feature to filter products by name.

  1. 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);
    }
  1. 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...
    }
  1. Test the Search Functionality:


These exercises should provide a deeper understanding of building robust RESTful services with Spring Boot.