Filters
Filters are middleware that run before matching routes. Register them with wildcard paths.
Registering Filters
Section titled “Registering Filters”Use wildcard paths to match multiple routes:
// Applies to all routes under /api/routesRegister.jsonFilter("/api/*", appState, new JwtFilter(jwtProvider));Filters run in registration order before the matching route handler. If any filter completes with an error result, the route handler is not invoked.
Creating a Filter
Section titled “Creating a Filter”A filter implements JsonFilter<APP> and receives an HttpStream<Void, APP>:
public class VisitedFilter implements JsonFilter<MyState> {
@Override public RequestPipeline<Void> handle(HttpStream<Void, MyState> e) { return e.complete(ctx -> { ctx.session().addResponseCookie(new HttpCookie("visited", "true")); return HttpResult.success(); }); }}Full Pipeline, No Request Body
Section titled “Full Pipeline, No Request Body”A filter’s stream is the same HttpStream type handlers receive, so the entire pipeline API is available: validate(), requireJwt(), map(), flatMap(), blockingMap(), blockingFlatMap(), asyncMap(), peek(), and so on. You can short-circuit a request (e.g. return 401 from flatMap) or enrich the downstream context before the route handler runs.
The one thing filters cannot do is read the JSON request body. The stream’s input type is Void, because a single filter can be registered against a wildcard path that matches many routes with different (or no) body types — there is no single type the framework can deserialize the body into. Calling ctx.in() inside a filter returns null.
Everything else on the request is available through ctx.session() and ctx.app():
- Request headers —
ctx.session().getRequestHeader("...") - Query parameters —
ctx.session().getQueryParam("...")(and viav.queryParam(...)invalidate()) - Path parameters —
ctx.session().getPathParam("...")(and viav.pathParam(...)invalidate()) - Cookies — read via
ctx.session(), write viaaddResponseCookie(...) - Response headers —
ctx.session().addResponseHeader(...) - Application state —
ctx.app() - JWT —
requireJwt(jwtProvider)works exactly as it does in a handler
public class JwtFilter implements JsonFilter<MyState> {
private final JwtProvider jwtProvider;
public JwtFilter(JwtProvider jwtProvider) { this.jwtProvider = jwtProvider; }
@Override public RequestPipeline<Void> handle(HttpStream<Void, MyState> e) { return e.requireJwt(jwtProvider).complete(); }}Because validate() lives on HttpStream, filters can also validate query and path parameters before requests reach the route — but they cannot validate body fields (there is no body object to validate).
return e .validate(v -> v.queryParam("tenant").required().matches("[a-z0-9-]+")) .complete();