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());