Java API for the Play Framework providing web application development capabilities including form handling, validation, dependency injection, and utility libraries
—
Play Framework's Routing DSL provides a fluent API for building type-safe routes programmatically with support for up to 3 parameters. The DSL enables dynamic route creation and HTTP method handling with automatic parameter binding and asynchronous request processing.
Main routing DSL class for creating programmatic routes with fluent method chaining.
/**
* DSL for building routers with type-safe parameter handling up to 3 parameters
*/
public class RoutingDsl {
/** Create GET route with path pattern */
public PathPatternMatcher GET(String pathPattern);
/** Create HEAD route with path pattern */
public PathPatternMatcher HEAD(String pathPattern);
/** Create POST route with path pattern */
public PathPatternMatcher POST(String pathPattern);
/** Create PUT route with path pattern */
public PathPatternMatcher PUT(String pathPattern);
/** Create DELETE route with path pattern */
public PathPatternMatcher DELETE(String pathPattern);
/** Create PATCH route with path pattern */
public PathPatternMatcher PATCH(String pathPattern);
/** Create OPTIONS route with path pattern */
public PathPatternMatcher OPTIONS(String pathPattern);
/** Create route with custom HTTP method */
public PathPatternMatcher match(String method, String pathPattern);
/** Build the configured router */
public play.api.routing.Router build();
}Usage Examples:
import play.routing.RoutingDsl;
import play.mvc.Result;
import static play.mvc.Results.*;
// Basic routing setup
public class MyRoutes {
public Router createRouter() {
RoutingDsl routingDsl = new RoutingDsl();
return routingDsl
.GET("/").routeTo(() -> ok("Welcome to my application"))
.GET("/health").routeTo(() -> ok("OK"))
.POST("/api/data").routeTo(() -> ok("Data received"))
.build();
}
}
// RESTful API routes
public class APIRoutes {
public Router createAPIRouter() {
RoutingDsl dsl = new RoutingDsl();
return dsl
// User routes
.GET("/api/users").routeTo(this::getAllUsers)
.GET("/api/users/:id").routeTo(this::getUser)
.POST("/api/users").routeTo(this::createUser)
.PUT("/api/users/:id").routeTo(this::updateUser)
.DELETE("/api/users/:id").routeTo(this::deleteUser)
// Product routes
.GET("/api/products").routeTo(this::getAllProducts)
.GET("/api/products/:id").routeTo(this::getProduct)
.POST("/api/products").routeTo(this::createProduct)
// Custom methods
.match("PATCH", "/api/users/:id/status").routeTo(this::updateUserStatus)
.build();
}
private Result getAllUsers() {
// Implementation
return ok("All users");
}
private Result getUser(String id) {
// Implementation with parameter
return ok("User: " + id);
}
// ... other methods
}Route pattern matcher supporting different parameter arities with both synchronous and asynchronous action handling.
/**
* Route pattern matcher for different parameter arities
*/
public class RoutingDsl.PathPatternMatcher {
/** Route with no parameters */
public RoutingDsl routeTo(F.Function0<Result> action);
/** Route with 1 parameter */
public <A1> RoutingDsl routeTo(F.Function<A1, Result> action);
/** Route with 2 parameters */
public <A1, A2> RoutingDsl routeTo(F.Function2<A1, A2, Result> action);
/** Route with 3 parameters */
public <A1, A2, A3> RoutingDsl routeTo(F.Function3<A1, A2, A3, Result> action);
/** Asynchronous route with no parameters */
public RoutingDsl routeAsync(F.Function0<F.Promise<Result>> action);
/** Asynchronous route with 1 parameter */
public <A1> RoutingDsl routeAsync(F.Function<A1, F.Promise<Result>> action);
/** Asynchronous route with 2 parameters */
public <A1, A2> RoutingDsl routeAsync(F.Function2<A1, A2, F.Promise<Result>> action);
/** Asynchronous route with 3 parameters */
public <A1, A2, A3> RoutingDsl routeAsync(F.Function3<A1, A2, A3, F.Promise<Result>> action);
}Usage Examples:
// Synchronous routes with parameters
public Router createSyncRoutes() {
RoutingDsl dsl = new RoutingDsl();
return dsl
// No parameters
.GET("/").routeTo(() -> ok("Home"))
// One parameter
.GET("/users/:id").routeTo((String id) ->
ok("User ID: " + id))
// Two parameters
.GET("/users/:userId/posts/:postId").routeTo((String userId, String postId) ->
ok("User: " + userId + ", Post: " + postId))
// Three parameters
.GET("/categories/:cat/products/:prod/reviews/:rev").routeTo(
(String cat, String prod, String rev) ->
ok("Category: " + cat + ", Product: " + prod + ", Review: " + rev))
.build();
}
// Asynchronous routes
public Router createAsyncRoutes() {
RoutingDsl dsl = new RoutingDsl();
return dsl
// Async no parameters
.GET("/async").routeAsync(() ->
F.Promise.promise(() -> ok("Async response")))
// Async with parameter
.GET("/async/users/:id").routeAsync((String id) ->
userService.findByIdAsync(id)
.map(user -> ok(Json.toJson(user))))
// Async with multiple parameters
.GET("/async/search/:type/:query").routeAsync((String type, String query) ->
searchService.searchAsync(type, query)
.map(results -> ok(Json.toJson(results))))
.build();
}// Custom parameter binding with type conversion
public class TypeSafeRoutes {
public Router createTypeSafeRouter() {
RoutingDsl dsl = new RoutingDsl();
return dsl
// String parameters (default)
.GET("/users/:id").routeTo(this::getUserString)
// Integer conversion
.GET("/products/:id").routeTo((String idStr) -> {
try {
Integer id = Integer.parseInt(idStr);
return getProduct(id);
} catch (NumberFormatException e) {
return badRequest("Invalid product ID");
}
})
// UUID conversion
.GET("/orders/:uuid").routeTo((String uuidStr) -> {
try {
UUID uuid = UUID.fromString(uuidStr);
return getOrder(uuid);
} catch (IllegalArgumentException e) {
return badRequest("Invalid UUID format");
}
})
.build();
}
private Result getUserString(String id) {
return ok("User: " + id);
}
private Result getProduct(Integer id) {
return ok("Product ID: " + id);
}
private Result getOrder(UUID uuid) {
return ok("Order UUID: " + uuid);
}
}// Comprehensive REST API builder
public class RESTAPIBuilder {
private final RoutingDsl dsl = new RoutingDsl();
public <T> RESTAPIBuilder resource(String basePath, RESTController<T> controller) {
dsl.GET(basePath).routeTo(controller::index)
.GET(basePath + "/:id").routeTo(controller::show)
.POST(basePath).routeTo(controller::create)
.PUT(basePath + "/:id").routeTo(controller::update)
.DELETE(basePath + "/:id").routeTo(controller::delete);
return this;
}
public <T> RESTAPIBuilder asyncResource(String basePath, AsyncRESTController<T> controller) {
dsl.GET(basePath).routeAsync(controller::indexAsync)
.GET(basePath + "/:id").routeAsync(controller::showAsync)
.POST(basePath).routeAsync(controller::createAsync)
.PUT(basePath + "/:id").routeAsync(controller::updateAsync)
.DELETE(basePath + "/:id").routeAsync(controller::deleteAsync);
return this;
}
public Router build() {
return dsl.build();
}
}
// REST controller interface
public interface RESTController<T> {
Result index();
Result show(String id);
Result create();
Result update(String id);
Result delete(String id);
}
// Async REST controller interface
public interface AsyncRESTController<T> {
F.Promise<Result> indexAsync();
F.Promise<Result> showAsync(String id);
F.Promise<Result> createAsync();
F.Promise<Result> updateAsync(String id);
F.Promise<Result> deleteAsync(String id);
}
// Usage
public Router createRESTAPI() {
return new RESTAPIBuilder()
.resource("/api/users", new UserController())
.resource("/api/products", new ProductController())
.asyncResource("/api/orders", new OrderController())
.build();
}// Route-specific middleware using action composition
public class MiddlewareRoutes {
public Router createProtectedRoutes() {
RoutingDsl dsl = new RoutingDsl();
return dsl
// Public routes
.GET("/").routeTo(() -> ok("Public home"))
.GET("/login").routeTo(() -> ok("Login page"))
// Protected routes with authentication
.GET("/dashboard").routeTo(() ->
withAuth(() -> ok("Dashboard")))
.GET("/profile/:id").routeTo((String id) ->
withAuth(() -> getUserProfile(id)))
// Admin routes with role check
.GET("/admin").routeTo(() ->
withRole("admin", () -> ok("Admin panel")))
.DELETE("/admin/users/:id").routeTo((String id) ->
withRole("admin", () -> deleteUser(id)))
.build();
}
private Result withAuth(Supplier<Result> action) {
if (isAuthenticated()) {
return action.get();
} else {
return unauthorized("Authentication required");
}
}
private Result withRole(String role, Supplier<Result> action) {
if (isAuthenticated() && hasRole(role)) {
return action.get();
} else {
return forbidden("Insufficient permissions");
}
}
private boolean isAuthenticated() {
// Check authentication
return true; // Placeholder
}
private boolean hasRole(String role) {
// Check user role
return true; // Placeholder
}
private Result getUserProfile(String id) {
return ok("Profile for user: " + id);
}
private Result deleteUser(String id) {
return ok("Deleted user: " + id);
}
}// Dynamic route generation based on configuration
public class DynamicRoutes {
public Router createDynamicRouter(Configuration config) {
RoutingDsl dsl = new RoutingDsl();
// Load route configurations
List<RouteConfig> routeConfigs = loadRouteConfigs(config);
for (RouteConfig routeConfig : routeConfigs) {
addRouteFromConfig(dsl, routeConfig);
}
return dsl.build();
}
private void addRouteFromConfig(RoutingDsl dsl, RouteConfig config) {
PathPatternMatcher matcher;
switch (config.method.toUpperCase()) {
case "GET":
matcher = dsl.GET(config.pattern);
break;
case "POST":
matcher = dsl.POST(config.pattern);
break;
case "PUT":
matcher = dsl.PUT(config.pattern);
break;
case "DELETE":
matcher = dsl.DELETE(config.pattern);
break;
default:
matcher = dsl.match(config.method, config.pattern);
}
// Add route handler based on parameter count
switch (config.parameterCount) {
case 0:
matcher.routeTo(() -> handleRoute(config));
break;
case 1:
matcher.routeTo((String p1) -> handleRoute(config, p1));
break;
case 2:
matcher.routeTo((String p1, String p2) -> handleRoute(config, p1, p2));
break;
case 3:
matcher.routeTo((String p1, String p2, String p3) -> handleRoute(config, p1, p2, p3));
break;
}
}
private List<RouteConfig> loadRouteConfigs(Configuration config) {
// Load from configuration
return new ArrayList<>(); // Placeholder
}
private Result handleRoute(RouteConfig config, String... params) {
// Dynamic route handling logic
return ok("Handled route: " + config.pattern + " with params: " + Arrays.toString(params));
}
private static class RouteConfig {
String method;
String pattern;
int parameterCount;
String handler;
}
}// Route-level error handling and validation
public class ValidatedRoutes {
public Router createValidatedRouter() {
RoutingDsl dsl = new RoutingDsl();
return dsl
// Validated single parameter
.GET("/users/:id").routeTo((String id) ->
validateAndHandle(id, this::isValidUserId, this::getUser))
// Validated multiple parameters
.GET("/posts/:userId/:postId").routeTo((String userId, String postId) ->
validateAndHandle2(userId, postId,
this::isValidUserId, this::isValidPostId,
this::getPost))
// Async validation
.GET("/async/validate/:id").routeAsync((String id) ->
validateAsync(id)
.thenCompose(valid -> {
if (valid) {
return F.Promise.promise(() -> ok("Valid: " + id));
} else {
return F.Promise.promise(() -> badRequest("Invalid: " + id));
}
}))
.build();
}
private Result validateAndHandle(String param, Predicate<String> validator,
Function<String, Result> handler) {
if (validator.test(param)) {
try {
return handler.apply(param);
} catch (Exception e) {
return internalServerError("Error processing: " + e.getMessage());
}
} else {
return badRequest("Invalid parameter: " + param);
}
}
private Result validateAndHandle2(String param1, String param2,
Predicate<String> validator1, Predicate<String> validator2,
BiFunction<String, String, Result> handler) {
if (validator1.test(param1) && validator2.test(param2)) {
try {
return handler.apply(param1, param2);
} catch (Exception e) {
return internalServerError("Error processing: " + e.getMessage());
}
} else {
return badRequest("Invalid parameters");
}
}
private F.Promise<Boolean> validateAsync(String param) {
return F.Promise.promise(() -> {
// Async validation logic
Thread.sleep(100); // Simulate async operation
return param != null && param.matches("\\d+");
});
}
private boolean isValidUserId(String id) {
return id != null && id.matches("\\d+");
}
private boolean isValidPostId(String id) {
return id != null && id.matches("\\d+");
}
private Result getUser(String id) {
return ok("User: " + id);
}
private Result getPost(String userId, String postId) {
return ok("User: " + userId + ", Post: " + postId);
}
}Install with Tessl CLI
npx tessl i tessl/maven-com-typesafe-play--play-java