Chain of Responsibility — show middleware-style request h… — Cracked Java
// Object-Oriented Programming · Behavioral Design Patterns
MidCoding

Chain of Responsibility — show middleware-style request handling.

Chain of Responsibility lets a request walk a linked sequence of handlers, each of which can process the request, stop the chain, or hand it down the line. Modern web middleware — Servlet filters, Spring interceptors, Express middleware — is exactly this pattern.

The handler interface

public interface RequestHandler {
    void handle(Request request, Chain chain);
}

public interface Chain {
    void proceed(Request request);
}

A handler receives the request and a reference to the rest of the chain. It can:

  • Decide the request is invalid and not call chain.proceed() (short-circuit).
  • Do work, then call chain.proceed() to pass control along.
  • Call chain.proceed() first, then do work after — useful for logging or wrapping a response.

A minimal chain

public final class HandlerChain implements Chain {
    private final List<RequestHandler> handlers;
    private final int index;

    public HandlerChain(List<RequestHandler> handlers) { this(handlers, 0); }
    private HandlerChain(List<RequestHandler> handlers, int i) {
        this.handlers = handlers; this.index = i;
    }

    public void proceed(Request request) {
        if (index >= handlers.size()) return; // end of chain
        Chain next = new HandlerChain(handlers, index + 1);
        handlers.get(index).handle(request, next);
    }
}

Each proceed() constructs a tiny HandlerChain pointing one step further. This makes the chain naturally re-entrant and stateless across requests.

Three handlers — auth, logging, business

public final class AuthHandler implements RequestHandler {
    public void handle(Request req, Chain chain) {
        if (!req.headers().containsKey("Authorization")) {
            req.reject(401, "missing auth"); // short-circuit, don't proceed
            return;
        }
        chain.proceed(req);
    }
}

public final class LoggingHandler implements RequestHandler {
    public void handle(Request req, Chain chain) {
        long start = System.nanoTime();
        try {
            chain.proceed(req); // run the rest first
        } finally {
            long ms = (System.nanoTime() - start) / 1_000_000;
            log.info("{} {} -> {} in {}ms", req.method(), req.path(), req.status(), ms);
        }
    }
}

public final class BusinessHandler implements RequestHandler {
    public void handle(Request req, Chain chain) {
        req.respond(200, doWork(req));
        // terminal handler: doesn't proceed
    }
    private String doWork(Request r) { return "ok"; }
}

// Wire them up — order matters
new HandlerChain(List.of(
    new LoggingHandler(),  // outermost: wraps everything for timing
    new AuthHandler(),     // gate
    new BusinessHandler()  // terminal
)).proceed(request);

The composition order is the request-flow order. Logging is outermost because it must observe both the auth rejection and the successful business response.

Where Spring and Servlet use this

  • Servlet FilterChain: identical shape. Each Filter.doFilter(req, res, chain) chooses whether to call chain.doFilter(req, res). The container builds the chain once per request from configured filters.
  • Spring HandlerInterceptor: similar but with three hooks — preHandle (return false to abort), postHandle (after controller, before view), afterCompletion (final cleanup). Spring drives the chain internally.
  • Spring Security FilterChainProxy: a chain of SecurityFilters — every authentication mechanism, CSRF check, and authorization rule is one link.
  • Netty ChannelPipeline: same pattern for inbound/outbound network events.

Why the pattern, not a list of ifs

A linear if (auth) { if (rate) { if (...) couples every check to the next and forces one team to own them all. Chain of Responsibility lets each handler be a separate class (separate test, separate team, separate deployment) and lets the runtime choose composition order. Adding a new check is one new class plus a registration — no edits to the existing handlers. Open/Closed in its purest form.

Mark your status