The Goal

Decouple the sender of a request from its receivers by giving multiple objects a chance to handle the request.

Instead of writing a massive if-else block to validate an incoming API request, you create a chain of standalone filters. If a filter passes, it hands the request to the next link. If it fails, the chain breaks immediately.

  1. The Handler Interface
public abstract class ApiFilter {
    private ApiFilter nextFilter;

    public void setNext(ApiFilter nextFilter) {
        this.nextFilter = nextFilter;
    }

    public void filter(String request) {
        if (handle(request) && nextFilter != null) {
            // Hand off to the next link in the chain
            nextFilter.filter(request);
        }
    }

    // Subclasses implement their specific logic here
    protected abstract boolean handle(String request);
}
  1. Concrete Handlers
public class RateLimitFilter extends ApiFilter {
    protected boolean handle(String request) {
        System.out.println("Rate limit check passed.");
        return true; 
    }
}
public class JwtAuthFilter extends ApiFilter {
    protected boolean handle(String request) {
        if (request.contains("Bearer")) {
            System.out.println("JWT Validated.");
            return true;
        }
        System.out.println("401 Unauthorized.");
        return false; // Breaks the chain!
    }
}
  1. Usage: Wiring the chain
ApiFilter rateLimiter = new RateLimitFilter();
ApiFilter auth = new JwtAuthFilter();

rateLimiter.setNext(auth); // Link them together
  1. The client just hits the first link
rateLimiter.filter("GET /api/v1/users Headers: Bearer abc123_");