Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Before and after handlers

Sometimes the same code is needed for many routes: log the request, add a header, check the login. Copying it into every handler is inconvenient. For this Javalin has before and after handlers: the first run before the main handler, the second after it. Other frameworks call them filters or middleware.

Execution order

Every request goes through the same chain:

  1. All matching before handlers, in the order they were declared.
  2. One main route handler (the endpoint).
  3. Exception and error handlers, if something went wrong.
  4. All matching after handlers — they run even after an error.

Logging every request

Add two handlers to the configuration block. before stores the start time in a request attribute, after prints the method, path, code and duration:

// src/main/java/com/example/blog/Main.java
config.routes.before(ctx -> ctx.attribute("start", System.nanoTime()));
 
config.routes.after(ctx -> {
    long start = ctx.attribute("start");
    long ms = (System.nanoTime() - start) / 1_000_000;
    System.out.println(ctx.method() + " " + ctx.path() + " -> " + ctx.statusCode() + " (" + ms + " ms)");
});

An attribute is a value that lives only during one request: ctx.attribute("name", value) stores it, ctx.attribute("name") reads it. Call a few addresses and look at the server console:

GET /posts -> 200 (5 ms)
GET /posts/99 -> 404 (2 ms)
POST /posts -> 201 (45 ms)
DELETE /posts/3 -> 204 (0 ms)

The first JSON request is always slower: on its first call Jackson prepares its internal structures.

Handlers for some addresses

You can pass a path as the first argument. The star * means "anything after this":

// src/main/java/com/example/blog/Main.java
config.routes.before("/posts/*", ctx -> System.out.println("Post request: " + ctx.path()));
config.routes.after(ctx -> ctx.header("X-Blog", "Javalin"));

The first handler runs for /posts/1 and /posts/new, the second adds the X-Blog: Javalin header to all responses, including 404 responses.

before and beforeMatched

A before handler runs for any request, even when there is no route and the response will be 404. If the code is needed only for existing routes, use beforeMatched and afterMatched. In chapter 4 we build a permission check on beforeMatched.

MethodWhen it runs
before, afterFor all requests
beforeMatched, afterMatchedOnly if a route or a static file was found

Common mistake

The path /posts in before matches only the address /posts, not everything that starts with it:

// src/main/java/com/example/blog/Main.java
config.routes.before("/posts", ctx -> System.out.println("before /posts: " + ctx.path()));

The request /posts prints the line, while the request /posts/1 does not. To cover nested addresses, add a second handler with the path /posts/*.

Thanks for reading!

Donate to the site — we work hard for you.