Routing

JWeb routing maps URLs to handlers. Handlers return Elements that are rendered as HTML, or Response objects for JSON/redirects.

Overview

Configure routes in a class implementing JWebRoutes. Routes support path parameters, query strings, and all HTTP methods.

@Component
public class Routes implements JWebRoutes {
    public void configure(JWeb app) {
        // Page routes
        app.pages("/", HomePage.class);

        // API routes
        app.get("/api/users", () -> Response.json(users));
        app.post("/api/users", req -> createUser(req));
    }
}

Page Routes

Register page classes that implement Template interface.

@Component
public class Routes implements JWebRoutes {
    public void configure(JWeb app) {
        app.pages(
            "/", HomePage.class,
            "/about", AboutPage.class,
            "/contact", ContactPage.class,
            "/pricing", PricingPage.class
        );
    }
}

Page Template

Each page is a class implementing Template.

public class HomePage implements Template {
    public Element render() {
        return main(
            section(
                h1("Welcome to JWeb"),
                p("Build web apps in pure Java")
            )
        );
    }
}

public class AboutPage implements Template {
    public Element render() {
        return main(
            h1("About Us"),
            p("We build Java web frameworks")
        );
    }
}

Path Parameters

Capture dynamic segments from the URL path.

// Single parameter
app.get("/users/:id", req -> {
    String id = req.param("id");
    User user = userService.findById(Long.parseLong(id));
    return userProfile(user);
});

// Multiple parameters
app.get("/posts/:category/:slug", req -> {
    String category = req.param("category");
    String slug = req.param("slug");
    Post post = postService.findBySlug(category, slug);
    return postPage(post);
});

// Optional with default
app.get("/blog/:page", req -> {
    int page = req.paramInt("page", 1);
    return blogListing(page);
});

Typed Parameters

Get parameters with automatic type conversion and validation.

// Integer parameters
app.get("/users/:id", req -> {
    int id = req.paramInt("id");        // Throws if not a number
    return userService.findById(id);
});

// Long for large IDs
app.get("/orders/:orderId", req -> {
    long orderId = req.paramLong("orderId");
    return orderService.findById(orderId);
});

// Double for decimals
app.get("/products/:price", req -> {
    double maxPrice = req.paramDouble("price");
    return productService.findUnder(maxPrice);
});

// Boolean flags
app.get("/posts/:featured", req -> {
    boolean featured = req.paramBool("featured");
    return postService.findByFeatured(featured);
});

// UUID for unique identifiers
app.get("/files/:fileId", req -> {
    UUID fileId = req.paramUUID("fileId");
    return fileService.findById(fileId);
});

Query Parameters

Access query string parameters from the URL.

// GET /search?q=java&page=2&sort=date
app.get("/search", req -> {
    String query = req.query("q");
    int page = req.queryInt("page", 1);
    String sort = req.query("sort", "relevance");

    List<Result> results = searchService.search(query, page, sort);
    return searchResults(results, query, page);
});

// Check if parameter exists
app.get("/products", req -> {
    boolean inStock = req.hasQuery("inStock");
    String category = req.query("category", "all");
    return productListing(category, inStock);
});

HTTP Methods

Handle different HTTP methods with dedicated methods.

// GET - retrieve data
app.get("/users", () -> userList());
app.get("/users/:id", req -> userDetail(req.param("id")));

// POST - create new resource
app.post("/users", req -> {
    User user = req.bodyAs(User.class);
    User saved = userService.save(user);
    return Response.redirect("/users/" + saved.getId());
});

// PUT - update entire resource
app.put("/users/:id", req -> {
    Long id = req.paramLong("id");
    User user = req.bodyAs(User.class);
    userService.update(id, user);
    return Response.ok();
});

// PATCH - partial update
app.patch("/users/:id", req -> {
    Long id = req.paramLong("id");
    Map<String, Object> updates = req.bodyAsMap();
    userService.partialUpdate(id, updates);
    return Response.ok();
});

// DELETE - remove resource
app.delete("/users/:id", req -> {
    userService.delete(req.paramLong("id"));
    return Response.ok();
});

Response Types

Return different response types from handlers.

// HTML response (default)
app.get("/page", () -> div("HTML content"));

// JSON response
app.get("/api/users", () -> Response.json(userService.findAll()));

// Redirect
app.get("/old-page", req -> Response.redirect("/new-page"));
app.post("/login", req -> {
    if (authenticate(req)) {
        return Response.redirect("/dashboard");
    }
    return Response.redirect("/login?error=true");
});

// Status codes
app.get("/not-found", req -> Response.notFound());
app.get("/forbidden", req -> Response.forbidden());
app.get("/error", req -> Response.serverError());

Custom Responses

// Custom status and headers
app.get("/custom", req -> Response.status(201)
    .header("X-Custom-Header", "value")
    .body("Created"));

// File download
app.get("/download/:file", req -> {
    byte[] data = fileService.read(req.param("file"));
    return Response.file(data, "report.pdf");
});

// Stream response
app.get("/stream", req -> Response.stream(outputStream -> {
    // Write to outputStream
}));

Middleware

Add middleware for authentication, logging, etc.

// Apply to all routes
app.use((req, next) -> {
    long start = System.currentTimeMillis();
    Object response = next.handle(req);
    long duration = System.currentTimeMillis() - start;
    logger.info("{} {} - {}ms", req.method(), req.path(), duration);
    return response;
});

// Apply to specific paths
app.use("/admin/**", Auth.required());
app.use("/api/**", Auth.jwt(SECRET_KEY));

// Multiple middleware
app.use("/api/**",
    RateLimit.perMinute(100),
    Cors.allow("*"),
    Auth.apiKey()
);

Custom Middleware

public class LoggingMiddleware implements Middleware {
    public Object handle(Request req, Next next) {
        logger.info("Request: {} {}", req.method(), req.path());
        try {
            Object response = next.handle(req);
            logger.info("Response: 200 OK");
            return response;
        } catch (Exception e) {
            logger.error("Error: {}", e.getMessage());
            throw e;
        }
    }
}

// Register
app.use(new LoggingMiddleware());