Skip to content

Interceptors

Interceptors run before or after request handlers. They are useful for authentication, logging, CORS headers, and other cross-cutting concerns.

Available Annotations

AnnotationWhen It RunsDefault Path
@BeforeBefore all requests* (wildcard)
@BeforeMatchedBefore matched routes only* (wildcard)
@AfterAfter all requests* (wildcard)
@AfterMatchedAfter matched routes only* (wildcard)

@Before

Runs before every request, even if no route matches:

java
@Endpoints("/api")
class UserEndpoints {

    @Before
    void logRequest(Context ctx) {
        System.out.println("Request: " + ctx.method() + " " + ctx.path());
    }

    @Get("/users")
    void getUsers(Context ctx) {
        ctx.result("Users");
    }

}

@BeforeMatched

Runs only when a route matches the request. Useful for authentication:

java
@BeforeMatched
void authenticate(Context ctx) {
    if (ctx.header("Authorization") == null) {
        throw new UnauthorizedResponse();
    }
}

@After

Runs after every request, regardless of whether a route matched:

java
@After
void addCorsHeaders(Context ctx) {
    ctx.header("Access-Control-Allow-Origin", "*");
}

@AfterMatched

Runs only after matched routes:

java
@AfterMatched
void logResponse(Context ctx) {
    System.out.println("Response: " + ctx.status());
}

Path Filtering

By default, interceptors apply to all paths (*). You can restrict them to specific paths:

java
@Before("/admin/*")
void requireAdmin(Context ctx) {
    if (!isAdmin(ctx)) {
        throw new ForbiddenResponse();
    }
}

Async Interceptors

Like HTTP method annotations, interceptors support async = true:

java
@Before(value = "*", async = true)
void asyncBefore(Context ctx) {
    // async processing
}

Ordering

Interceptors defined in the same @Endpoints class execute in declaration order. The plugin also detects potential conflicts when multiple @Before or @After handlers share the same path and warns you at startup.

Example: Authentication + Logging

java
@Endpoints("/api")
class SecuredEndpoints {

    @Before
    void logRequest(Context ctx) {
        ctx.attribute("requestStart", System.currentTimeMillis());
    }

    @BeforeMatched
    void authenticate(Context ctx) {
        String token = ctx.header("Authorization");
        if (token == null || !tokenService.isValid(token)) {
            throw new UnauthorizedResponse("Invalid token");
        }
    }

    @Get("/data")
    void getData(Context ctx) {
        ctx.json(dataService.getAll());
    }

    @AfterMatched
    void logDuration(Context ctx) {
        long start = ctx.attribute("requestStart");
        long duration = System.currentTimeMillis() - start;
        System.out.println("Request took: " + duration + "ms");
    }

}

Released under the Apache 2.0 License.