3.2 REST Controllers & Endpoint Mapping
← Back to Chapter 3: REST API & Web Layer
Building RESTful services requires clear URL design, correct HTTP verb semantics, and structured controller architecture.
@Controller vs @RestController
| Feature | @Controller | @RestController |
|---|---|---|
| Primary Use Case | Used mainly for MVC/web pages | Used mainly for REST APIs |
| Return Type | Usually returns a view/page name | Usually returns data such as JSON/XML |
| Serialization | Does not automatically serialize return value to JSON | Automatically serializes return value to JSON |
| @ResponseBody | If you want JSON, commonly use @ResponseBody | @ResponseBody is included automatically |
| Ecosystem / Tech | Used with technologies like Thymeleaf/JSP | Commonly used in Spring Boot microservices |
// Traditional MVC: Returns view name resolved by ViewResolver (Thymeleaf/JSP)
@Controller
public class WebViewController {
@GetMapping("/home")
public String index(Model model) {
model.addAttribute("title", "Dashboard");
return "home"; // resolves to home.html
}
}
// Modern REST: Method return value written directly to HTTP response as JSON
@RestController // Equals @Controller + @ResponseBody on every method
@RequestMapping("/api/v1/users")
public class UserRestController {
@GetMapping("/{id}")
public UserResponse getUser(@PathVariable Long id) {
return userService.findById(id); // Jackson serializes to {"id": 1, ...}
}
}
HTTP Method Mappings & REST Semantics
Spring MVC offers shorthand composite annotations for HTTP verbs:
| Annotation | HTTP Method | Idempotent? | Safe? | Typical REST Usage |
|---|---|---|---|---|
@GetMapping | GET | Yes | Yes | Retrieve a resource or collection |
@PostMapping | POST | No | No | Create a new resource or execute non-idempotent action |
@PutMapping | PUT | Yes | No | Completely replace/overwrite an existing resource |
@PatchMapping | PATCH | No | No | Partially update specific fields of a resource |
@DeleteMapping | DELETE | Yes | No | Delete a resource |
@RestController
@RequestMapping("/api/v1/products")
public class ProductController {
private final ProductService productService;
public ProductController(ProductService productService) {
this.productService = productService;
}
@GetMapping
public List<ProductDTO> listAll() {
return productService.getAll();
}
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public ProductDTO create(@Valid @RequestBody CreateProductRequest req) {
return productService.create(req);
}
@PutMapping("/{id}")
public ProductDTO replace(@PathVariable Long id, @Valid @RequestBody UpdateProductRequest req) {
return productService.replace(id, req);
}
@PatchMapping("/{id}")
public ProductDTO partialUpdate(@PathVariable Long id, @RequestBody Map<String, Object> fields) {
return productService.patch(id, fields);
}
@DeleteMapping("/{id}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void delete(@PathVariable Long id) {
productService.delete(id);
}
}
Indempotency
Idempotent means: If you perform the same request multiple times, the final state of the server remains the same as after performing it once.
| HTTP Method | Idempotent? | Why? | Example |
|---|---|---|---|
| GET | ✅ Yes | It only retrieves data and does not change server state. | GET /users/10 |
| POST | ❌ No | Repeating it can create multiple resources or perform the operation multiple times. | POST /users → may create multiple users |
| PUT | ✅ Yes | Repeating the same request updates/replaces the resource with the same data. | PUT /users/10 |
| PATCH | ⚠️ Usually No | It partially updates a resource, and repeating the operation may cause different results depending on the operation. | PATCH /account → increment balance |
| DELETE | ✅ Yes | Deleting the same resource multiple times results in the resource remaining deleted. | DELETE /users/10 |
| HEAD | ✅ Yes | Like GET but returns headers without the response body; it doesn't change server state. | HEAD /users/10 |
| OPTIONS | ✅ Yes | It retrieves supported communication options and doesn't modify the resource. | OPTIONS /users |