π§ What REALLY Happens When You Hit a Spring Boot REST API?
π₯ Inside the Hidden World of Filters, Interceptors & DispatcherServlet
π¬ Intro: “It’s Just a Simple API Call…” — Said Every Developer Before Debugging It
You hit this:
GET http://localhost:8080/api/user/42
π― And your controller returns the user info. Done, right?
π LOL no.
Welcome to Spring Boot, where your request goes on a rollercoaster ride through filters, interceptors, servlet containers, exception resolvers, and more — before it even thinks about reaching your controller.
Let’s deep-dive into the secret life of a REST API call in Spring Boot — with π internal mechanics, π± common mistakes
⚙️ 1. πͺ The Request Arrives at... The Bouncer (Tomcat)
Spring Boot has an embedded server (usually Tomcat). When your app starts, it's like opening a door at a nightclub. The request knocks. πΊ
Tomcat says:
"Who dis? π€¨"
And then it lets the HTTP request enter.
π§ Technically: Tomcat parses the socket connection and sends the request into the Servlet layer.
π§° 2. π Filters Step In – “We Check Everyone!”
@Component
public class MyFilter implements Filter {
public void doFilter(...) {
// CORS? Auth header? Logging? All here
chain.doFilter(request, response);
}
}
π What They Do:
- Logging πͺ΅
- Header checks π§Ύ
- CORS setup π
- JWT parsing π
⚠️ Can’t access Spring beans easily (they live in the servlet world π).
Joke:
“Filters are like your mom at the door. If your hair’s messy or token is missing, she won’t let you go to the party.” π
π§ 3. π© Enter the DispatcherServlet — The Real Boss
Spring's legendary DispatcherServlet is the Front Controller.
It’s the guy in the suit standing behind the scenes, pointing where every request should go.
@Bean
public DispatcherServlet dispatcherServlet() {
return new DispatcherServlet();
}
π§ Responsibilities:
- Picks the right controller π€
- Handles exceptions π₯
- Sends responses π
- Makes sure your REST endpoint actually gets called
π¦ 4. π΅️ Interceptors Arrive – “We See What You Do”
Unlike filters, interceptors live in Springland, where you get DI, beans, and control. π
@Component
public class MyInterceptor implements HandlerInterceptor {
public boolean preHandle(...) { }
public void postHandle(...) { }
public void afterCompletion(...) { }
}
✅ preHandle() — Before Controller
- Block request? Return
false. π« - Start timing? ⏱️
- Check session? π
π postHandle() — After Controller
- Modify
ModelAndViewπ - Log what controller returned ✅
- Inject extra data before view is rendered π
π« You can't modify the JSON response here if it’s a REST controller.
π afterCompletion() — After Response Sent
- Log request duration ⏱️
- Cleanup (ThreadLocal, MDC) π§Ή
- Log exceptions π€
❌ You cannot modify the response here; it’s already committed!
π― 5. π― Handler Mapping – “Where’s This Request Supposed to Go?”
Spring finds the matching controller based on:
- URL path π£️
- HTTP method π
- Annotations like
@GetMapping,@PostMapping
@GetMapping("/api/user/{id}")
public User getUser(@PathVariable Long id) {
return userService.findUser(id);
}
π€ 6. Your Controller Finally Runs π
Spring injects everything you love:
@PathVariable,@RequestParam,@RequestBody- Autowired beans
- RequestContext, Headers, etc.
π£ 7. Boom! Exceptions? Enter @ControllerAdvice π¬
@ControllerAdvice
public class MyErrorHandler {
@ExceptionHandler(UserNotFoundException.class)
public ResponseEntity<?> handleIt() {
return ResponseEntity.status(404).body("No user, bro!");
}
}
✅ Spring checks for matching exception handlers.
❌ If none are found → you get a white-label error page (uglyyyy).
π€ 8. Response Goes Back (Backwards Flow)
The response now goes through:
ResponseBodyAdviceπ ️- Interceptors:
postHandle()→afterCompletion()π - Servlet Filters (again) π¦
- Embedded server (Tomcat) ✉️
- The client gets the final response π
πΊ️ The Full Lifecycle Map
Client
↓
π Embedded Server (Tomcat)
↓
π Filters (Servlet API)
↓
π© DispatcherServlet
↓
π΅️ Interceptor.preHandle()
↓
π§ Handler Mapping
↓
π― Controller Logic
↓
π§Ή Interceptor.postHandle()
↓
π¦ ResponseBodyAdvice
↓
π§Ό Interceptor.afterCompletion()
↓
π Filters (post)
↓
✉️ Response to Client
π― Should I Set Headers in a Filter or an Interceptor? π€
Great question! You might be wondering:
“If I can add headers in a filter, why should I ever use an interceptor?” π€¨
π ️ Short Answer:
✅ Both can add headers.
π« But filters don’t know which controller is being called.
π‘ Interceptors do, and they have full Spring context.
π¬ Case Studies to Compare
1️⃣ Global Headers — Use a Filter ✅
// Filter example
response.setHeader("X-App-Version", "1.0.9");
response.setHeader("X-Frame-Options", "DENY");
Perfect for:
- CORS headers π
- Security headers π
- Trace IDs π§΅
2️⃣ Controller-Specific Headers — Use an Interceptor π‘
// Interceptor example
public void postHandle(...) {
if (handler instanceof HandlerMethod hm &&
hm.hasMethodAnnotation(MySpecialHeader.class)) {
response.setHeader("X-Special", "true");
}
}
Interceptors know:
- Which controller method is running π§
- What annotations are present π
- Can access Spring beans! π±
3️⃣ Modify Headers After Controller — Use Interceptor or ResponseBodyAdvice π
If your controller is REST and you need to add headers *after* business logic:
- ✅ Interceptor’s
postHandle()(for headers) - ✅
ResponseBodyAdvice(for body + headers㪠Interviewer May Ask:
“Can you set headers in a filter?” ✅ Yes, but only generic ones.
“Can you modify headers based on the controller?” ❌ Not in filters. ✅ Use interceptors!
π― Rule of Thumb:
- Use filters when you need to do something BEFORE Spring kicks in
- Use interceptors when you want access to Spring context, beans, and controller logic
π Bonus Tip:
For REST APIs, add metadata headers like:
response.setHeader("X-Request-ID", requestId);
response.setHeader("X-User-ID", userId);
This helps in:
- π Distributed tracing
- π Debugging logs across microservices
- π Monitoring APIs under load
π¬ What About You?
Ever confused filters and interceptors in a live project or interview? π
Did someone add response headers in afterCompletion() and wonder why they didn't show up?
Share your war stories in the comments below π
π§ͺ Interview Questions You’ll Crush After Reading This
| ❓ Question | ✅ Answer |
|---|---|
| Filter vs Interceptor? | Filter = Servlet level Interceptor = Spring MVC level |
Can I block request in preHandle()? |
Yes, return false to stop the flow |
Can I modify response in afterCompletion()? |
❌ No. Response already committed |
| Where to log time taken? | afterCompletion() is ideal ⏱️ |
| Filter throws exception. Who handles it? | Global exception handler won’t catch unless inside Spring context |
πͺ Wrapping Up: You Just Became a Request Flow Wizard π§
Most devs debug controller issues without knowing filters or interceptors even touched the request.
But not you. πͺ You now know exactly who does what, when, and why.
Whether it’s logging, security, tracing, or performance — this internal flow gives you superpowers in both interviews and real projects.
π¬ Your Turn:
Ever spent hours debugging a request that mysteriously vanished? Was it a filter, a missing header, or a short-circuiting preHandle()? π Drop your war stories in the comments π
Comments
Post a Comment