Implementing Request Filtering and Interception

1. Creating Servlet Filters (Filter interface)

Example: Request timing filter

public class TimingFilter implements Filter {
  @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
      throws IOException, ServletException {
    long start = System.nanoTime();
    chain.doFilter(req, res);
    long ms = (System.nanoTime() - start) / 1_000_000;
    ((HttpServletResponse)res).addHeader("X-Elapsed-Ms", String.valueOf(ms));
  }
}

2. Registering Filters with FilterRegistrationBean

Example: Register filter for /api/* with order

@Bean
FilterRegistrationBean<TimingFilter> timingFilter() {
  FilterRegistrationBean<TimingFilter> reg = new FilterRegistrationBean<>(new TimingFilter());
  reg.addUrlPatterns("/api/*");
  reg.setOrder(Ordered.HIGHEST_PRECEDENCE);
  reg.setName("timingFilter");
  return reg;
}

3. Ordering Multiple Filters (@Order)

Source Mechanism
@Order(n) On Filter @Component
FilterRegistrationBean.setOrder() Programmatic
Ordered.HIGHEST_PRECEDENCE Run earliest

4. Creating HandlerInterceptors

Example: Audit interceptor recording start time

public class AuditInterceptor implements HandlerInterceptor {
  @Override public boolean preHandle(HttpServletRequest req, HttpServletResponse res, Object handler) {
    req.setAttribute("startedAt", Instant.now());
    return true; // false to short-circuit
  }
  @Override public void afterCompletion(HttpServletRequest req, HttpServletResponse res,
                                        Object handler, Exception ex) {
    Instant t = (Instant) req.getAttribute("startedAt");
    log.info("path={} status={} ms={}", req.getRequestURI(), res.getStatus(),
             Duration.between(t, Instant.now()).toMillis());
  }
}

5. Registering Interceptors

Example: Register interceptor with path exclusion

@Configuration
public class WebConfig implements WebMvcConfigurer {
  @Override public void addInterceptors(InterceptorRegistry r) {
    r.addInterceptor(new AuditInterceptor())
     .addPathPatterns("/api/**")
     .excludePathPatterns("/api/health");
  }
}

6. Using preHandle, postHandle, afterCompletion

Method When
preHandle Before handler — return false to abort
postHandle After handler, before view rendering
afterCompletion After response sent (always)

7. Implementing Request Logging Filter

Example: Configure Commons request logging filter

@Bean
CommonsRequestLoggingFilter requestLogging() {
  CommonsRequestLoggingFilter f = new CommonsRequestLoggingFilter();
  f.setIncludeQueryString(true);
  f.setIncludePayload(true);
  f.setMaxPayloadLength(2048);
  f.setIncludeHeaders(false);
  return f;
}
// Enable: logging.level.org.springframework.web.filter.CommonsRequestLoggingFilter=DEBUG

8. Implementing CORS Filter

Example: CORS filter bean

@Bean
CorsFilter corsFilter() {
  CorsConfiguration cfg = new CorsConfiguration();
  cfg.setAllowedOrigins(List.of("https://app.example.com"));
  cfg.setAllowedMethods(List.of("GET","POST","PUT","DELETE"));
  cfg.setAllowedHeaders(List.of("*"));
  cfg.setMaxAge(3600L);
  UrlBasedCorsConfigurationSource src = new UrlBasedCorsConfigurationSource();
  src.registerCorsConfiguration("/**", cfg);
  return new CorsFilter(src);
}

9. Implementing Rate Limiting Filter

Example: Per-IP rate limit filter with Bucket4j

@Component
public class RateLimitFilter extends OncePerRequestFilter {
  private final Map<String, Bucket> buckets = new ConcurrentHashMap<>();
  @Override protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain)
      throws IOException, ServletException {
    Bucket b = buckets.computeIfAbsent(req.getRemoteAddr(),
      k -> Bucket.builder().addLimit(Bandwidth.simple(100, Duration.ofMinutes(1))).build());
    if (b.tryConsume(1)) chain.doFilter(req, res);
    else { res.setStatus(429); res.getWriter().write("Too Many Requests"); }
  }
}

10. Handling Filter Exceptions

Warning: Exceptions in filters bypass @ControllerAdvice. Catch and write error response inside the filter, or use HandlerExceptionResolver.
Strategy How
Try/catch in filter Write JSON error directly
Delegate to resolver Inject HandlerExceptionResolver & call resolveException
Rely on /error Set res.sendError(status)

11. Using OncePerRequestFilter for Single Execution

Issue Solution
Filter runs on async dispatch / forwards Extend OncePerRequestFilter
Skip async/error dispatches Override shouldNotFilterAsyncDispatch()
Skip specific paths Override shouldNotFilter(req)