Skip to main content

πŸ’₯ “Filter vs Interceptor in Spring — Who’s the Real Gatekeeper?”

πŸ’₯ “Filter vs Interceptor in Spring — Who’s the Real Gatekeeper?”

πŸ” The Hidden Guards of Your REST APIs (with Real Examples, Jokes & Weird Discoveries!)


🎯 1) What Is Filter / Interceptor / OncePerRequestFilter?

🧹 Filter

A Filter is a component from the Servlet API that sits at the very beginning of a request's journey. It is invoked before the Spring context even gets involved. It's great for tasks like CORS configuration, encoding setup, basic logging, and blocking or modifying raw requests/responses.

🎀 Think of Filter as the club bouncer who checks everyone at the entrance—before they even see the dance floor!

πŸ•΅️ Interceptor

A HandlerInterceptor is a Spring MVC feature that works after the request has passed through the DispatcherServlet but before the controller method is executed. It can inspect the handler (controller) method, add attributes, or block the request if needed.

πŸ€“ It’s like the guy inside the club checking if you’re VIP before letting you in to the private room (controller logic). πŸŽ‰

🧼 OncePerRequestFilter

This is a special Spring class that extends Filter but ensures the filter logic is run only once per request, even if the request is forwarded or internally dispatched again (like /error).

πŸ” OncePerRequestFilter is the chill bouncer that says “I’ve already checked this guy—no need to scan again.” πŸ’³


πŸ› ️ 2) When to Use Each Concept (With Examples)

πŸ§ͺ Use Filter when:

You’re dealing with low-level request/response operations that must happen before Spring even touches the request.

🧾 Real-Time Example: CORS Header Filter

@Component
public class CorsHeaderFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        HttpServletResponse res = (HttpServletResponse) response;
        res.setHeader("Access-Control-Allow-Origin", "*");
        res.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
        res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
        chain.doFilter(request, response);
    }
}

🎯 Use this when your frontend (like React/Angular) is calling your backend from a different domain — browsers will block requests unless these headers are present.

πŸ›‘️ Another Real-Time Example: Security Headers Filter

@Component
public class SecurityHeadersFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        HttpServletResponse res = (HttpServletResponse) response;
        res.setHeader("X-Content-Type-Options", "nosniff");
        res.setHeader("X-Frame-Options", "DENY");
        res.setHeader("X-XSS-Protection", "1; mode=block");
        chain.doFilter(request, response);
    }
}

🚫 Prevents clickjacking, XSS, and sneaky browser tricks.

πŸ“¦ Use cases: CORS, security headers, GZIP compression, request throttling, IP blocking, etc.

πŸ“Œ Fun Fact: Filters run even when there's no controller mapped. They're the true bodyguards. πŸ’ͺ


πŸ”„ Use OncePerRequestFilter when:

You want to filter requests in Spring but avoid multiple executions during internal dispatches.

πŸ” Example: JWT Token Validation

@Component
public class JwtAuthFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
      throws ServletException, IOException {
        String token = request.getHeader("Authorization");
        // Validate token
        chain.doFilter(request, response);
    }
}

Avoids double logging or duplicate security logic during error forwarding.

πŸ’‘ Why use it? Because twice is once too many.


🧭 Use Interceptor when:

You need access to the controller method, parameters, or want to modify the model/view.

πŸ“‹ Example: Logging controller methods using preHandle()

@Component
public class LoggingInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        if (handler instanceof HandlerMethod method) {
            System.out.println("Invoking: " + method.getMethod().getName());
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
        response.setHeader("X-Custom-Header", "Intercepted");
    }
}

🧠 Use preHandle() for logging or pre-checks, and postHandle() for response customizations.

🎯 Best when you care about Spring MVC context — controller metadata, annotations, model attributes.


🚨 3) Common Mistakes You Might Be Making 😱

❌ Assuming Filters & Interceptors are interchangeable — they are NOT.
❌ Writing token validation in a regular Filter and wondering why it runs twice.
❌ Trying to access controller parameters in a Filter (spoiler: you can’t).
❌ Forgetting to add your Interceptor to the WebMvcConfigurer.

πŸ˜… “I thought my logging was clean… until I saw every error logged twice. Once for the call, and once for the error page.”


🧬 4) Unknown Truths You Probably Didn't Know

🀯 Bonus: Aren’t Filters, Interceptors, and OncePerRequestFilter All Just Called Once Per API?

Excellent question! It seems like all of them only run once per request… but here’s the plot twist πŸ”€:

✅ Yes, normal Filters and Interceptors usually run once per request… BUT:

That’s only true when the request completes normally.

🧱 If Spring internally forwards a request (like to /error), a regular Filter runs again, causing duplicate logs or validations!

πŸ§ͺ Imagine:

  • /api/data fails → Spring forwards to /error

  • Your filter runs again for /error

  • Logs twice, tokens rechecked, chaos! 😡‍πŸ’«

πŸ’‘ OncePerRequestFilter to the Rescue:

It marks the request as already filtered:

request.setAttribute("__once_per_request_filter_applied__", true);

So Spring skips it on internal forwards. Clean and safe.

🎯 Rule: If it must run once and only once, use OncePerRequestFilter.


πŸ“š 5) Interview Questions and Answers — Spring Filter vs Interceptor vs OncePerRequestFilter

❓ Common Real-World Question: Can All Three (Filter, Interceptor, OncePerRequestFilter) Customize Request and Response?

✅ Short Answer: Yes — but not all in the same way or at the same point in the lifecycle!

🧠 Deep Dive:

🧠 Deep Dive: Comparison Table

Mechanism Can Modify Request? Can Modify Response? When in Lifecycle?
Filter ✅ Yes ✅ Yes Before DispatcherServlet
OncePerRequestFilter ✅ Yes ✅ Yes Same as Filter but once only
Interceptor ⚠️ Limited ✅ Yes (postHandle) After controller mapping

πŸ’¬ Example Q: “How do you modify the response after the controller returns?”
πŸ’‘ A: Use postHandle() of an Interceptor.

πŸ’¬ Example Q: “How do you inject CORS headers?”
πŸ’‘ A: Use a Filter (or OncePerRequestFilter).

πŸ’¬ Example Q: “How do you ensure the logic runs once even during /error?”
πŸ’‘ A: Use OncePerRequestFilter.

🎀 Never say “All 3 are same.” Say “Each has different superpowers.” 🦸

πŸ“Š Spring Request Flow Diagram — Filter vs Interceptor vs Controller

Let me break it into a diagrammatic sequence showing how and when each component runs:


πŸ”½ Incoming HTTP Request │ ▼ ┌─────────────────────────────────────┐ │ 🌐 Servlet Filter │ ◄────── Regular Filter └─────────────────────────────────────┘ │ ┌─────────────────────────────────────┐ │ ✅ OncePerRequestFilter (if used) │ ◄── Smart Filter (executes only once) └─────────────────────────────────────┘ │ ▼ πŸšͺ DispatcherServlet (Spring entry point) │ ▼ ┌────────────────────────────────────────────┐ │ πŸ•΅️ HandlerInterceptor - preHandle() │ ◄── Before Controller method └────────────────────────────────────────────┘ │ ▼ 🧠 Controller method is executed │ ▼ ┌────────────────────────────────────────────┐ │ πŸ•΅️ HandlerInterceptor - postHandle() │ ◄── After controller, before view render └────────────────────────────────────────────┘ │ ▼ πŸ–¨️ Response generated and sent back

🧠 Explanation:

Component Runs Before Runs After Can Access Controller? Runs on /error forward? Main Use
Filter ✔️ ✔️ ✔️ (yes) CORS, Security, Logging
OncePerRequestFilter ✔️ (once) ✔️ ❌ (no, once only) Avoid double execution
Interceptor ✔️ ✔️ ✔️ (HandlerMethod) ✔️ Add attributes, Logging


🀯 When /error or internal forwarding happens...


/api/secure-data (fails) ──┬──→ DispatcherServlet │ └─→ Forwards to /error │ ⚠️ Regular Filter runs again (duplicate logs!) ✅ OncePerRequestFilter skips re-execution

🧾 Labels :

  • ✅ Filter → πŸ’¬ "Raw request modification — runs before Spring."

  • ✅ OncePerRequestFilter → 🧠 "Like Filter, but only once per request."

  • ✅ Interceptor → πŸ§ͺ "Knows about controllers. You can check handler, annotations."

  • ✅ DispatcherServlet → πŸšͺ “Main gate to Spring MVC — routes to controllers.” 


🧹 6) Wrapping Up — Final Thoughts 🎬

Spring gives us all these tools for a reason. If you:

  • Want to control raw requests/responses πŸ‘‰ go with Filter or OncePerRequestFilter

  • Want to tweak stuff after controller logic πŸ‘‰ use Interceptor

πŸ” Just don’t mix them up or you’ll end up debugging “phantom filters” at 2 AM. πŸŒ™

πŸ’‘ “Filter = Bouncer. Interceptor = VIP checker. OncePerRequestFilter = Smart bouncer who remembers you.”

Have you faced filter madness or interceptor illusions in your project? πŸ§™‍♂️
Drop your stories or surprises below πŸ‘‡ — Let’s debug life together!


πŸ“’ Blog by Anandharaj — Sharing what I wish someone told me years ago. πŸ˜…

πŸ”— Follow my blog for more Java deep dives!

Comments

Popular posts from this blog

πŸ” Is final Really Final in Java? The Truth May Surprise You 😲

πŸ’¬ “When I was exploring what to do and what not to do in Java, one small keyword caught my eye — final . I thought it meant: locked, sealed, frozen — like my fridge when I forget to defrost it.”   But guess what? Java has its own meaning of final… and it’s not always what you expect! πŸ˜… Let’s break it down together — with code, questions, confusion, jokes, and everything in between. 🎯 The Confusing Case: You Said It's Final... Then It Changed?! 🫠 final List<String> names = new ArrayList <>(); names.add( "Anand" ); names.add( "Rahul" ); System.out.println(names); // [Anand, Rahul] 🀯 Hold on... that’s final , right?! So how on earth is it still changing ? Time to dive deeper... 🧠 Why Is It Designed Like This? Here’s the key secret: In Java, final applies to the reference , not the object it points to . Let’s decode this like a spy mission πŸ•΅️‍♂️: Imagine This: final List<String> names = new ArrayList <>(); Be...

🌟 My Journey – From Zero to Senior Java Tech Lead 🌟

 There’s one thing I truly believe… If I can become a Java developer, then anyone in the world can. πŸ’― Sounds crazy? Let me take you back. πŸ•“ Back in 2015… I had zero coding knowledge . Not just that — I had no interest in coding either. But life has its own plans. In 2016, I got a chance to move to Bangalore and joined a Java course at a training center. That’s where it all started — Every day, every session made me feel like: "Ohhh! Even I can be a developer!" That course didn’t just teach Java — it gave me confidence . πŸ§ͺ Two Life-Changing Incidents 1️⃣ The Interview That Wasn't Planned Halfway through my course, I had to urgently travel to Chennai to donate blood to a family member. After that emotional rollercoaster, I found myself reflecting on my skills and the future. The next day, as I was preparing for my move to Bangalore to complete the remaining four months of my course, I randomly thought — "Let me test my skills... let me just see...

🎒 Java Loops: Fun, Fear, and ForEach() Fails

πŸŒ€ Oops, I Looped It Again! — The Ultimate Java Loop Guide You Won't Forget “I remember this question from one of my early interviews — I was just 2 years into Java and the interviewer asked, ‘Which loop do you prefer and why?’” At first, I thought, “Duh! for-each is cleaner.” But then he grilled me with cases where it fails. 😡 That led me to explore all loop types, their powers, and their pitfalls. Let’s deep-dive into every major Java loop with examples &  real-world guidance so you'll never forget again. πŸ” Loop Type #1: Classic For Loop — “The Old Reliable” ✅ When to Use: You need an index You want to iterate in reverse You want full control over loop mechanics ✅ Good Example: List<String> names = List.of("A", "B", "C"); for (int i = 0; i < names.size(); i++) { System.out.println(i + ": " + names.get(i)); } πŸ”₯ Reverse + Removal Example: List<String> item...