Skip to content
SP StackPractices
intermediate By StackPractices

Front Controller Pattern

Route all incoming requests through a single handler that dispatches to the appropriate page command, centralizing request processing and security.

Topics: design

Note: This guide follows English-language naming conventions and terminology standards common in international development teams. Examples use English identifiers and comments to maximize compatibility across codebases and tooling.

Front Controller Pattern

Overview

The Front Controller Pattern routes all incoming requests through a single entry point — a central handler that processes each request and dispatches it to the appropriate command or controller. Rather than having every page handle its own request parsing, authentication, and logging, the Front Controller centralizes these cross-cutting concerns.

This is the pattern behind virtually every modern web framework. Spring Boot’s DispatcherServlet, Django’s WSGI handler, and Express.js middleware chains all embody Front Controller principles. One handler receives every request, performs common processing, then delegates to a page-specific controller.

When to Use

Use the Front Controller Pattern when:

  • Multiple pages share common preprocessing (authentication, logging, encoding)
  • You want a clean URL routing system independent of file structure
  • Request handling needs to be consistent across the entire application
  • You need centralized error handling and security checks

When to Avoid

  • Very simple applications with only a few pages (overhead without benefit)
  • Static sites where each page can be served directly
  • When the front controller becomes a God object handling too many responsibilities

Solution

Python

from typing import Callable, Dict
from http.server import BaseHTTPRequestHandler, HTTPServer

class Request:
    def __init__(self, path: str, method: str, headers: dict):
        self.path = path
        self.method = method
        self.headers = headers

class Response:
    def __init__(self, body: str, status: int = 200):
        self.body = body
        self.status = status

class FrontController:
    def __init__(self):
        self._handlers: Dict[str, Callable] = {}

    def register(self, path: str, handler: Callable):
        self._handlers[path] = handler

    def dispatch(self, request: Request) -> Response:
        # Common preprocessing
        if not self._authenticate(request):
            return Response("Unauthorized", 401)

        handler = self._handlers.get(request.path)
        if handler:
            return handler(request)
        return Response("Not Found", 404)

    def _authenticate(self, request: Request) -> bool:
        return request.headers.get("Authorization") == "Bearer valid"


def home_handler(request: Request) -> Response:
    return Response("Welcome to the home page")

def user_handler(request: Request) -> Response:
    return Response("User profile page")


# Setup
controller = FrontController()
controller.register("/", home_handler)
controller.register("/user", user_handler)

# Usage
req = Request("/user", "GET", {"Authorization": "Bearer valid"})
resp = controller.dispatch(req)
print(resp.status, resp.body)

Java

import java.util.*;

public record Request(String path, String method, Map<String, String> headers) {}
public record Response(String body, int status) {
    public Response(String body) { this(body, 200); }
}

@FunctionalInterface
interface RequestHandler {
    Response handle(Request request);
}

class FrontController {
    private final Map<String, RequestHandler> handlers = new HashMap<>();

    public void register(String path, RequestHandler handler) {
        handlers.put(path, handler);
    }

    public Response dispatch(Request request) {
        if (!authenticate(request)) {
            return new Response("Unauthorized", 401);
        }
        RequestHandler handler = handlers.get(request.path());
        if (handler != null) {
            return handler.handle(request);
        }
        return new Response("Not Found", 404);
    }

    private boolean authenticate(Request request) {
        return "Bearer valid".equals(request.headers().get("Authorization"));
    }
}

// Handlers
class HomeHandler implements RequestHandler {
    public Response handle(Request request) {
        return new Response("Welcome to the home page");
    }
}

class UserHandler implements RequestHandler {
    public Response handle(Request request) {
        return new Response("User profile page");
    }
}

// Usage
FrontController controller = new FrontController();
controller.register("/", new HomeHandler());
controller.register("/user", new UserHandler());

Request req = new Request("/user", "GET", Map.of("Authorization", "Bearer valid"));
Response resp = controller.dispatch(req);
System.out.println(resp.status() + " " + resp.body());

JavaScript

class Request {
  constructor(path, method, headers) {
    this.path = path;
    this.method = method;
    this.headers = headers;
  }
}

class Response {
  constructor(body, status = 200) {
    this.body = body;
    this.status = status;
  }
}

class FrontController {
  constructor() {
    this.handlers = new Map();
  }

  register(path, handler) {
    this.handlers.set(path, handler);
  }

  dispatch(request) {
    if (!this.authenticate(request)) {
      return new Response('Unauthorized', 401);
    }
    const handler = this.handlers.get(request.path);
    if (handler) {
      return handler(request);
    }
    return new Response('Not Found', 404);
  }

  authenticate(request) {
    return request.headers.authorization === 'Bearer valid';
  }
}

// Handlers
const homeHandler = (req) => new Response('Welcome to the home page');
const userHandler = (req) => new Response('User profile page');

// Setup
const controller = new FrontController();
controller.register('/', homeHandler);
controller.register('/user', userHandler);

// Usage
const req = new Request('/user', 'GET', { authorization: 'Bearer valid' });
const resp = controller.dispatch(req);
console.log(resp.status, resp.body);

Explanation

The Front Controller centralizes:

  • Request parsing: URL decoding, parameter extraction, content negotiation
  • Preprocessing: Authentication, authorization, input validation
  • Routing: Mapping URLs to the correct page controller
  • Postprocessing: Logging, metrics, response formatting
  • Error handling: Converting exceptions into HTTP status codes

Variants

VariantMechanismUse Case
Servlet FilterChain of filters in Java web appsAuthentication, encoding, compression
MiddlewareExpress.js / Django middleware stackCross-cutting request processing
DispatcherSpring MVC DispatcherServletFull MVC framework with view resolution
Reverse ProxyNginx / Apache as front controllerLoad balancing, SSL termination, caching

Best Practices

  • Keep the controller lean. Delegate page logic to dedicated command objects or sub-controllers.
  • Use a routing table. Map URL patterns to handlers declaratively rather than with nested if-else.
  • Process common concerns first. Authentication, CSRF protection, and input sanitization belong here.
  • Support interceptors. Allow middleware to modify requests and responses without changing the core.
  • Return early for errors. Invalid requests should fail fast before reaching page logic.

Common Mistakes

  • Bloated Front Controller that knows too much about page logic. It should dispatch, not implement.
  • Tight coupling to a specific view technology. The controller should not generate HTML directly.
  • Ignoring HTTP semantics. Returning 200 for every response hides errors from clients.
  • Missing error boundaries. Unhandled exceptions leak stack traces to users.
  • Synchronous blocking. The front controller should not perform long-running operations inline.

Real-World Examples

Spring MVC

DispatcherServlet is the Front Controller. It receives all HTTP requests, resolves controllers via annotations, and delegates view rendering to ViewResolver.

Django

Django’s WSGI application acts as a Front Controller. URL routing (urls.py) maps incoming requests to views after middleware processing.

Express.js

Express apps use a central app object with middleware. app.use(auth) and app.get('/user', handler) build a Front Controller pipeline.

Frequently Asked Questions

Q: What is the difference between Front Controller and Page Controller? A: Page Controller uses one controller per page. Front Controller uses a single entry point for the entire application.

Q: Does every web framework use Front Controller? A: Most modern frameworks do. PHP’s original model (one file per page) is the alternative Page Controller approach.

Q: Can a Front Controller handle WebSocket connections? A: Yes. The entry point inspects the upgrade header and routes to a WebSocket handler or an HTTP handler accordingly.