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
@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) |