Middleware

Intercept requests before and after controller execution with simple annotations.

Basic usage
@Before(LoggingMiddleware.class)
@GET(value = "/dashboard", name = "dashboard")
private Object dashboard(Request req, Response res) {
    return render("dashboard.html", Map.of());
}
Chain multiple middlewares
@Before({CorsMiddleware.class, ApiKeyMiddleware.class, RateLimitMiddleware.class})
@GET(value = "/api/data", name = "api.data")
private Object data(Request req, Response res) {
    // 1. CORS headers added
    // 2. API key validated  (401 otherwise)
    // 3. Rate limit checked (429 otherwise)
    // 4. Controller executed
    return "{ \"data\": [] }";
}
Before and After
@Before(TimingMiddleware.class)
@After(TimingMiddleware.class)
@GET(value = "/profile", name = "profile")
private Object profile(Request req, Response res) {
    return render("profile.html", Map.of());
}
Built-in middlewares
Middleware Description Headers
LoggingMiddleware Logs each request with IP and User-Agent.
Debugging, monitoring, audit trail
CorsMiddleware Adds CORS headers to allow cross-origin requests.
Public APIs, separate frontends, mobile apps
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization, X-CSRF-TOKEN
RateLimitMiddleware Limits to 100 requests/minute per IP. Returns 429 if exceeded.
Anti-spam, anti-DDoS, public endpoints
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 87
TimingMiddleware Measures execution time.
Performance optimization, detecting slow routes
X-Response-Time: 42ms
ApiKeyMiddleware Verifies the X-API-Key header. Returns 401 if invalid.
Securing APIs, simple authentication
Create a custom middleware
public class MaintenanceMiddleware implements Middleware {

    @Override
    public void handle(Request req, Response res) throws Exception {
        boolean isMaintenance = System.getenv("MAINTENANCE_MODE") != null;

        if (isMaintenance && !req.pathInfo().equals("/maintenance")) {
            res.redirect("/maintenance");
            throw new Exception("Maintenance mode");
        }
    }
}

@Before(MaintenanceMiddleware.class)
@GET("/dashboard")
private Object dashboard(Request req, Response res) {
    return render("dashboard.html", Map.of());
}
💡 Execution order
  • @Before middlewares execute in array order
  • • Controller executes next
  • @After middlewares execute after the controller
  • • If a middleware throws an exception, execution stops immediately
  • • Middlewares are instantiated once (singleton)