๐ฐ Building Web Controllers in Spring
The Story of the Royal Mail Room
๐ญ Our Magical Metaphor
Imagine a grand castle that receives letters from people all over the kingdom. Every letter needs to go to the right person, get answered, and be sent back. This is exactly how Spring Web Controllers work!
- The Castle = Your Spring Application
- The Mail Room = DispatcherServlet
- Delivery Workers = Controllers
- Letter Instructions = Annotations
- Opening Letters = Request Data Extraction
- Writing Replies = Response Handling
๐ฌ Part 1: The DispatcherServlet - The Head Mail Sorter
What Is It?
Think of the DispatcherServlet as the HEAD OF THE MAIL ROOM. Every single letter (HTTP request) that arrives at the castle MUST pass through this person first.
What Does It Do?
Letter arrives โ Head Sorter reads address โ
Finds right worker โ Worker writes reply โ
Head Sorter sends it back
The Magic Flow
graph TD A["๐ฉ Request Arrives"] --> B["๐งโ๐ผ DispatcherServlet"] B --> C["๐ Find Right Controller"] C --> D["โ๏ธ Controller Does Work"] D --> E["๐ค Response Goes Back"] E --> F["๐ค User Gets Answer"]
Simple Example
When you type www.myshop.com/products in your browser:
- DispatcherServlet catches this request
- Looks at
/productsand thinks: โWho handles products?โ - Finds
ProductController - Sends the request there
- Gets the response and sends it to you
You never see DispatcherServlet. It works silently behind the scenes!
๐๏ธ Part 2: MVC Architecture - The Three Best Friends
Meet the Team
Model - View - Controller
Think of them as three friends working together to answer every letter:
| Friend | Job | Castle Role |
|---|---|---|
| Model | Carries data | The messenger bag |
| View | Makes things pretty | The calligrapher |
| Controller | Makes decisions | The brain |
How They Work Together
graph TD A["๐ค User Request"] --> B["๐ฎ Controller"] B --> C["๐ฆ Model - Gets Data"] C --> D["๐จ View - Makes Pretty"] D --> E["๐ค User Sees Page"]
Real Example
User wants to see their profile:
- Controller receives request: โShow profile!โ
- Model fetches data: name, email, photo
- View creates HTML page with that data
- User sees beautiful profile page!
@Controller
public class ProfileController {
@GetMapping("/profile")
public String showProfile(Model model) {
// Model carries the data
model.addAttribute("name", "Alex");
model.addAttribute("email", "alex@mail.com");
// View name to display
return "profile-page";
}
}
๐ท Part 3: Controller Types - Different Workers for Different Jobs
Two Types of Mail Workers
1. Regular Controller (@Controller)
- Returns web pages (HTML)
- Good for websites people visit
- Works with Views (templates)
@Controller
public class WebsiteController {
@GetMapping("/home")
public String homePage() {
return "home"; // Returns home.html
}
}
2. REST Controller (@RestController)
- Returns data (JSON/XML)
- Good for mobile apps & APIs
- No views needed!
@RestController
public class ApiController {
@GetMapping("/api/users")
public List<User> getUsers() {
return userService.findAll();
// Returns JSON data directly
}
}
Quick Comparison
| Feature | @Controller | @RestController |
|---|---|---|
| Returns | HTML pages | JSON/XML data |
| Used for | Websites | APIs, Mobile apps |
| Needs Views? | Yes | No |
Secret Tip! ๐คซ
@RestController = @Controller + @ResponseBody
Itโs just a shortcut!
๐ท๏ธ Part 4: Request Mapping - Address Labels on Letters
The Big Boss: @RequestMapping
This tells Spring: โSend requests from THIS address to THIS methodโ
@RequestMapping("/shop")
public class ShopController {
// All paths start with /shop
}
The Shortcut Family
Instead of writing long annotations, use these shortcuts:
| Annotation | What It Means | Use When |
|---|---|---|
@GetMapping |
GET request | Reading data |
@PostMapping |
POST request | Creating data |
@PutMapping |
PUT request | Updating data |
@DeleteMapping |
DELETE request | Deleting data |
@PatchMapping |
PATCH request | Partial update |
Examples That Make Sense
@RestController
@RequestMapping("/books")
public class BookController {
@GetMapping
public List<Book> getAllBooks() {
// GET /books
}
@GetMapping("/{id}")
public Book getBook(@PathVariable Long id) {
// GET /books/5
}
@PostMapping
public Book createBook(@RequestBody Book book) {
// POST /books
}
@DeleteMapping("/{id}")
public void deleteBook(@PathVariable Long id) {
// DELETE /books/5
}
}
Path Variables - Names in the Address
Use {curlyBraces} to capture parts of the URL:
@GetMapping("/users/{userId}/posts/{postId}")
public Post getPost(
@PathVariable Long userId,
@PathVariable Long postId) {
// URL: /users/42/posts/7
// userId = 42, postId = 7
}
๐จ Part 5: Request Data Extraction - Opening the Letters
Where Does Data Come From?
Data can arrive in many ways. Spring has tools to catch ALL of them!
graph TD A["๐จ Incoming Request"] --> B["URL Path"] A --> C["Query Parameters"] A --> D["Request Body"] A --> E["Headers"] A --> F["Cookies"]
1. @PathVariable - Data in the URL Path
// URL: /products/laptop/reviews/5
@GetMapping("/products/{product}/reviews/{reviewId}")
public Review getReview(
@PathVariable String product,
@PathVariable Long reviewId) {
// product = "laptop"
// reviewId = 5
}
2. @RequestParam - Data After the ?
// URL: /search?keyword=phone&page=2
@GetMapping("/search")
public List<Product> search(
@RequestParam String keyword,
@RequestParam(defaultValue = "1") int page) {
// keyword = "phone"
// page = 2
}
Optional parameters:
@RequestParam(required = false) String filter
3. @RequestBody - Data Inside the Letter
For POST/PUT requests with JSON data:
@PostMapping("/users")
public User createUser(@RequestBody User user) {
// JSON automatically becomes User object!
// {"name": "Alex", "email": "alex@mail.com"}
// โ User(name="Alex", email="alex@mail.com")
}
4. @RequestHeader - Info on the Envelope
@GetMapping("/secure-data")
public Data getData(
@RequestHeader("Authorization") String token,
@RequestHeader("Accept-Language") String lang) {
// Read special instructions!
}
5. @CookieValue - Saved Notes
@GetMapping("/preferences")
public Settings getSettings(
@CookieValue("theme") String theme) {
// Read saved preferences
}
All Together Now!
@PostMapping("/orders/{orderId}/items")
public OrderItem addItem(
@PathVariable Long orderId,
@RequestParam boolean express,
@RequestBody Item item,
@RequestHeader("User-Id") Long userId) {
// All data extracted automatically!
}
๐ค Part 6: Request & Response Handling - The Complete Story
The Request Journey
graph TD A["๐ Browser/App"] -->|HTTP Request| B["DispatcherServlet"] B -->|Route| C["Controller Method"] C -->|Process| D["Service Layer"] D -->|Fetch| E["Database"] E -->|Data| D D -->|Result| C C -->|Response| B B -->|HTTP Response| A
Response Types
1. Return a View (HTML Page)
@Controller
public class PageController {
@GetMapping("/welcome")
public String welcome(Model model) {
model.addAttribute("message", "Hello!");
return "welcome"; // โ welcome.html
}
}
2. Return Data (JSON)
@RestController
public class ApiController {
@GetMapping("/api/user")
public User getUser() {
return new User("Alex", "alex@mail.com");
// โ {"name":"Alex","email":"alex@mail.com"}
}
}
3. Return ResponseEntity (Full Control)
@GetMapping("/api/product/{id}")
public ResponseEntity<Product> getProduct(
@PathVariable Long id) {
Product product = service.findById(id);
if (product == null) {
return ResponseEntity.notFound().build();
// Returns 404 status
}
return ResponseEntity.ok(product);
// Returns 200 with product data
}
Setting Response Status
@PostMapping("/users")
@ResponseStatus(HttpStatus.CREATED) // 201
public User createUser(@RequestBody User user) {
return service.save(user);
}
Common HTTP Status Codes
| Code | Meaning | When to Use |
|---|---|---|
| 200 | OK | Success! |
| 201 | Created | New thing made |
| 400 | Bad Request | User sent wrong data |
| 404 | Not Found | Thing doesnโt exist |
| 500 | Server Error | Something broke |
Handling Errors Gracefully
@RestController
public class ProductController {
@GetMapping("/products/{id}")
public ResponseEntity<Product> getProduct(
@PathVariable Long id) {
try {
Product p = service.findById(id);
return ResponseEntity.ok(p);
} catch (NotFoundException e) {
return ResponseEntity
.status(HttpStatus.NOT_FOUND)
.body(null);
}
}
}
๐ฏ The Complete Picture
@RestController
@RequestMapping("/api/orders")
public class OrderController {
private final OrderService service;
// Constructor injection
public OrderController(OrderService service) {
this.service = service;
}
// GET /api/orders
@GetMapping
public List<Order> getAllOrders() {
return service.findAll();
}
// GET /api/orders/42
@GetMapping("/{id}")
public ResponseEntity<Order> getOrder(
@PathVariable Long id) {
Order order = service.findById(id);
if (order == null) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok(order);
}
// POST /api/orders
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public Order createOrder(
@RequestBody Order order) {
return service.save(order);
}
// DELETE /api/orders/42
@DeleteMapping("/{id}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void deleteOrder(@PathVariable Long id) {
service.delete(id);
}
}
๐ Remember This!
-
DispatcherServlet = The traffic cop. Routes all requests.
-
MVC = Model carries data, View shows it, Controller decides.
-
Two Controllers:
@Controllerfor pages,@RestControllerfor APIs. -
Mapping Shortcuts:
@GetMapping,@PostMapping, etc. -
Data Extraction:
@PathVariable,@RequestParam,@RequestBody,@RequestHeader -
Responses: Return views, data, or
ResponseEntityfor full control.
๐ You Did It!
You now understand how Spring handles web requests from start to finish. Like a well-organized castle mail room, Springโs web controllers:
- Receive requests (letters arrive)
- Route them properly (DispatcherServlet)
- Process them (Controllers)
- Return responses (send replies)
Go build something amazing! ๐
