or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

dependency-injection.mdform-processing.mdformatting.mdindex.mdrouting.mdstreaming.mdutilities.mdvalidation.md

routing.mddocs/

0

# Routing DSL

1

2

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.

3

4

## Capabilities

5

6

### Core Routing DSL

7

8

Main routing DSL class for creating programmatic routes with fluent method chaining.

9

10

```java { .api }

11

/**

12

* DSL for building routers with type-safe parameter handling up to 3 parameters

13

*/

14

public class RoutingDsl {

15

/** Create GET route with path pattern */

16

public PathPatternMatcher GET(String pathPattern);

17

18

/** Create HEAD route with path pattern */

19

public PathPatternMatcher HEAD(String pathPattern);

20

21

/** Create POST route with path pattern */

22

public PathPatternMatcher POST(String pathPattern);

23

24

/** Create PUT route with path pattern */

25

public PathPatternMatcher PUT(String pathPattern);

26

27

/** Create DELETE route with path pattern */

28

public PathPatternMatcher DELETE(String pathPattern);

29

30

/** Create PATCH route with path pattern */

31

public PathPatternMatcher PATCH(String pathPattern);

32

33

/** Create OPTIONS route with path pattern */

34

public PathPatternMatcher OPTIONS(String pathPattern);

35

36

/** Create route with custom HTTP method */

37

public PathPatternMatcher match(String method, String pathPattern);

38

39

/** Build the configured router */

40

public play.api.routing.Router build();

41

}

42

```

43

44

**Usage Examples:**

45

46

```java

47

import play.routing.RoutingDsl;

48

import play.mvc.Result;

49

import static play.mvc.Results.*;

50

51

// Basic routing setup

52

public class MyRoutes {

53

54

public Router createRouter() {

55

RoutingDsl routingDsl = new RoutingDsl();

56

57

return routingDsl

58

.GET("/").routeTo(() -> ok("Welcome to my application"))

59

.GET("/health").routeTo(() -> ok("OK"))

60

.POST("/api/data").routeTo(() -> ok("Data received"))

61

.build();

62

}

63

}

64

65

// RESTful API routes

66

public class APIRoutes {

67

68

public Router createAPIRouter() {

69

RoutingDsl dsl = new RoutingDsl();

70

71

return dsl

72

// User routes

73

.GET("/api/users").routeTo(this::getAllUsers)

74

.GET("/api/users/:id").routeTo(this::getUser)

75

.POST("/api/users").routeTo(this::createUser)

76

.PUT("/api/users/:id").routeTo(this::updateUser)

77

.DELETE("/api/users/:id").routeTo(this::deleteUser)

78

79

// Product routes

80

.GET("/api/products").routeTo(this::getAllProducts)

81

.GET("/api/products/:id").routeTo(this::getProduct)

82

.POST("/api/products").routeTo(this::createProduct)

83

84

// Custom methods

85

.match("PATCH", "/api/users/:id/status").routeTo(this::updateUserStatus)

86

.build();

87

}

88

89

private Result getAllUsers() {

90

// Implementation

91

return ok("All users");

92

}

93

94

private Result getUser(String id) {

95

// Implementation with parameter

96

return ok("User: " + id);

97

}

98

99

// ... other methods

100

}

101

```

102

103

### Path Pattern Matcher

104

105

Route pattern matcher supporting different parameter arities with both synchronous and asynchronous action handling.

106

107

```java { .api }

108

/**

109

* Route pattern matcher for different parameter arities

110

*/

111

public class RoutingDsl.PathPatternMatcher {

112

/** Route with no parameters */

113

public RoutingDsl routeTo(F.Function0<Result> action);

114

115

/** Route with 1 parameter */

116

public <A1> RoutingDsl routeTo(F.Function<A1, Result> action);

117

118

/** Route with 2 parameters */

119

public <A1, A2> RoutingDsl routeTo(F.Function2<A1, A2, Result> action);

120

121

/** Route with 3 parameters */

122

public <A1, A2, A3> RoutingDsl routeTo(F.Function3<A1, A2, A3, Result> action);

123

124

/** Asynchronous route with no parameters */

125

public RoutingDsl routeAsync(F.Function0<F.Promise<Result>> action);

126

127

/** Asynchronous route with 1 parameter */

128

public <A1> RoutingDsl routeAsync(F.Function<A1, F.Promise<Result>> action);

129

130

/** Asynchronous route with 2 parameters */

131

public <A1, A2> RoutingDsl routeAsync(F.Function2<A1, A2, F.Promise<Result>> action);

132

133

/** Asynchronous route with 3 parameters */

134

public <A1, A2, A3> RoutingDsl routeAsync(F.Function3<A1, A2, A3, F.Promise<Result>> action);

135

}

136

```

137

138

**Usage Examples:**

139

140

```java

141

// Synchronous routes with parameters

142

public Router createSyncRoutes() {

143

RoutingDsl dsl = new RoutingDsl();

144

145

return dsl

146

// No parameters

147

.GET("/").routeTo(() -> ok("Home"))

148

149

// One parameter

150

.GET("/users/:id").routeTo((String id) ->

151

ok("User ID: " + id))

152

153

// Two parameters

154

.GET("/users/:userId/posts/:postId").routeTo((String userId, String postId) ->

155

ok("User: " + userId + ", Post: " + postId))

156

157

// Three parameters

158

.GET("/categories/:cat/products/:prod/reviews/:rev").routeTo(

159

(String cat, String prod, String rev) ->

160

ok("Category: " + cat + ", Product: " + prod + ", Review: " + rev))

161

162

.build();

163

}

164

165

// Asynchronous routes

166

public Router createAsyncRoutes() {

167

RoutingDsl dsl = new RoutingDsl();

168

169

return dsl

170

// Async no parameters

171

.GET("/async").routeAsync(() ->

172

F.Promise.promise(() -> ok("Async response")))

173

174

// Async with parameter

175

.GET("/async/users/:id").routeAsync((String id) ->

176

userService.findByIdAsync(id)

177

.map(user -> ok(Json.toJson(user))))

178

179

// Async with multiple parameters

180

.GET("/async/search/:type/:query").routeAsync((String type, String query) ->

181

searchService.searchAsync(type, query)

182

.map(results -> ok(Json.toJson(results))))

183

184

.build();

185

}

186

```

187

188

## Advanced Usage Patterns

189

190

### Type-Safe Parameter Handling

191

192

```java

193

// Custom parameter binding with type conversion

194

public class TypeSafeRoutes {

195

196

public Router createTypeSafeRouter() {

197

RoutingDsl dsl = new RoutingDsl();

198

199

return dsl

200

// String parameters (default)

201

.GET("/users/:id").routeTo(this::getUserString)

202

203

// Integer conversion

204

.GET("/products/:id").routeTo((String idStr) -> {

205

try {

206

Integer id = Integer.parseInt(idStr);

207

return getProduct(id);

208

} catch (NumberFormatException e) {

209

return badRequest("Invalid product ID");

210

}

211

})

212

213

// UUID conversion

214

.GET("/orders/:uuid").routeTo((String uuidStr) -> {

215

try {

216

UUID uuid = UUID.fromString(uuidStr);

217

return getOrder(uuid);

218

} catch (IllegalArgumentException e) {

219

return badRequest("Invalid UUID format");

220

}

221

})

222

223

.build();

224

}

225

226

private Result getUserString(String id) {

227

return ok("User: " + id);

228

}

229

230

private Result getProduct(Integer id) {

231

return ok("Product ID: " + id);

232

}

233

234

private Result getOrder(UUID uuid) {

235

return ok("Order UUID: " + uuid);

236

}

237

}

238

```

239

240

### REST API Builder

241

242

```java

243

// Comprehensive REST API builder

244

public class RESTAPIBuilder {

245

246

private final RoutingDsl dsl = new RoutingDsl();

247

248

public <T> RESTAPIBuilder resource(String basePath, RESTController<T> controller) {

249

dsl.GET(basePath).routeTo(controller::index)

250

.GET(basePath + "/:id").routeTo(controller::show)

251

.POST(basePath).routeTo(controller::create)

252

.PUT(basePath + "/:id").routeTo(controller::update)

253

.DELETE(basePath + "/:id").routeTo(controller::delete);

254

255

return this;

256

}

257

258

public <T> RESTAPIBuilder asyncResource(String basePath, AsyncRESTController<T> controller) {

259

dsl.GET(basePath).routeAsync(controller::indexAsync)

260

.GET(basePath + "/:id").routeAsync(controller::showAsync)

261

.POST(basePath).routeAsync(controller::createAsync)

262

.PUT(basePath + "/:id").routeAsync(controller::updateAsync)

263

.DELETE(basePath + "/:id").routeAsync(controller::deleteAsync);

264

265

return this;

266

}

267

268

public Router build() {

269

return dsl.build();

270

}

271

}

272

273

// REST controller interface

274

public interface RESTController<T> {

275

Result index();

276

Result show(String id);

277

Result create();

278

Result update(String id);

279

Result delete(String id);

280

}

281

282

// Async REST controller interface

283

public interface AsyncRESTController<T> {

284

F.Promise<Result> indexAsync();

285

F.Promise<Result> showAsync(String id);

286

F.Promise<Result> createAsync();

287

F.Promise<Result> updateAsync(String id);

288

F.Promise<Result> deleteAsync(String id);

289

}

290

291

// Usage

292

public Router createRESTAPI() {

293

return new RESTAPIBuilder()

294

.resource("/api/users", new UserController())

295

.resource("/api/products", new ProductController())

296

.asyncResource("/api/orders", new OrderController())

297

.build();

298

}

299

```

300

301

### Middleware and Filters

302

303

```java

304

// Route-specific middleware using action composition

305

public class MiddlewareRoutes {

306

307

public Router createProtectedRoutes() {

308

RoutingDsl dsl = new RoutingDsl();

309

310

return dsl

311

// Public routes

312

.GET("/").routeTo(() -> ok("Public home"))

313

.GET("/login").routeTo(() -> ok("Login page"))

314

315

// Protected routes with authentication

316

.GET("/dashboard").routeTo(() ->

317

withAuth(() -> ok("Dashboard")))

318

319

.GET("/profile/:id").routeTo((String id) ->

320

withAuth(() -> getUserProfile(id)))

321

322

// Admin routes with role check

323

.GET("/admin").routeTo(() ->

324

withRole("admin", () -> ok("Admin panel")))

325

326

.DELETE("/admin/users/:id").routeTo((String id) ->

327

withRole("admin", () -> deleteUser(id)))

328

329

.build();

330

}

331

332

private Result withAuth(Supplier<Result> action) {

333

if (isAuthenticated()) {

334

return action.get();

335

} else {

336

return unauthorized("Authentication required");

337

}

338

}

339

340

private Result withRole(String role, Supplier<Result> action) {

341

if (isAuthenticated() && hasRole(role)) {

342

return action.get();

343

} else {

344

return forbidden("Insufficient permissions");

345

}

346

}

347

348

private boolean isAuthenticated() {

349

// Check authentication

350

return true; // Placeholder

351

}

352

353

private boolean hasRole(String role) {

354

// Check user role

355

return true; // Placeholder

356

}

357

358

private Result getUserProfile(String id) {

359

return ok("Profile for user: " + id);

360

}

361

362

private Result deleteUser(String id) {

363

return ok("Deleted user: " + id);

364

}

365

}

366

```

367

368

### Dynamic Route Generation

369

370

```java

371

// Dynamic route generation based on configuration

372

public class DynamicRoutes {

373

374

public Router createDynamicRouter(Configuration config) {

375

RoutingDsl dsl = new RoutingDsl();

376

377

// Load route configurations

378

List<RouteConfig> routeConfigs = loadRouteConfigs(config);

379

380

for (RouteConfig routeConfig : routeConfigs) {

381

addRouteFromConfig(dsl, routeConfig);

382

}

383

384

return dsl.build();

385

}

386

387

private void addRouteFromConfig(RoutingDsl dsl, RouteConfig config) {

388

PathPatternMatcher matcher;

389

390

switch (config.method.toUpperCase()) {

391

case "GET":

392

matcher = dsl.GET(config.pattern);

393

break;

394

case "POST":

395

matcher = dsl.POST(config.pattern);

396

break;

397

case "PUT":

398

matcher = dsl.PUT(config.pattern);

399

break;

400

case "DELETE":

401

matcher = dsl.DELETE(config.pattern);

402

break;

403

default:

404

matcher = dsl.match(config.method, config.pattern);

405

}

406

407

// Add route handler based on parameter count

408

switch (config.parameterCount) {

409

case 0:

410

matcher.routeTo(() -> handleRoute(config));

411

break;

412

case 1:

413

matcher.routeTo((String p1) -> handleRoute(config, p1));

414

break;

415

case 2:

416

matcher.routeTo((String p1, String p2) -> handleRoute(config, p1, p2));

417

break;

418

case 3:

419

matcher.routeTo((String p1, String p2, String p3) -> handleRoute(config, p1, p2, p3));

420

break;

421

}

422

}

423

424

private List<RouteConfig> loadRouteConfigs(Configuration config) {

425

// Load from configuration

426

return new ArrayList<>(); // Placeholder

427

}

428

429

private Result handleRoute(RouteConfig config, String... params) {

430

// Dynamic route handling logic

431

return ok("Handled route: " + config.pattern + " with params: " + Arrays.toString(params));

432

}

433

434

private static class RouteConfig {

435

String method;

436

String pattern;

437

int parameterCount;

438

String handler;

439

}

440

}

441

```

442

443

### Error Handling and Validation

444

445

```java

446

// Route-level error handling and validation

447

public class ValidatedRoutes {

448

449

public Router createValidatedRouter() {

450

RoutingDsl dsl = new RoutingDsl();

451

452

return dsl

453

// Validated single parameter

454

.GET("/users/:id").routeTo((String id) ->

455

validateAndHandle(id, this::isValidUserId, this::getUser))

456

457

// Validated multiple parameters

458

.GET("/posts/:userId/:postId").routeTo((String userId, String postId) ->

459

validateAndHandle2(userId, postId,

460

this::isValidUserId, this::isValidPostId,

461

this::getPost))

462

463

// Async validation

464

.GET("/async/validate/:id").routeAsync((String id) ->

465

validateAsync(id)

466

.thenCompose(valid -> {

467

if (valid) {

468

return F.Promise.promise(() -> ok("Valid: " + id));

469

} else {

470

return F.Promise.promise(() -> badRequest("Invalid: " + id));

471

}

472

}))

473

474

.build();

475

}

476

477

private Result validateAndHandle(String param, Predicate<String> validator,

478

Function<String, Result> handler) {

479

if (validator.test(param)) {

480

try {

481

return handler.apply(param);

482

} catch (Exception e) {

483

return internalServerError("Error processing: " + e.getMessage());

484

}

485

} else {

486

return badRequest("Invalid parameter: " + param);

487

}

488

}

489

490

private Result validateAndHandle2(String param1, String param2,

491

Predicate<String> validator1, Predicate<String> validator2,

492

BiFunction<String, String, Result> handler) {

493

if (validator1.test(param1) && validator2.test(param2)) {

494

try {

495

return handler.apply(param1, param2);

496

} catch (Exception e) {

497

return internalServerError("Error processing: " + e.getMessage());

498

}

499

} else {

500

return badRequest("Invalid parameters");

501

}

502

}

503

504

private F.Promise<Boolean> validateAsync(String param) {

505

return F.Promise.promise(() -> {

506

// Async validation logic

507

Thread.sleep(100); // Simulate async operation

508

return param != null && param.matches("\\d+");

509

});

510

}

511

512

private boolean isValidUserId(String id) {

513

return id != null && id.matches("\\d+");

514

}

515

516

private boolean isValidPostId(String id) {

517

return id != null && id.matches("\\d+");

518

}

519

520

private Result getUser(String id) {

521

return ok("User: " + id);

522

}

523

524

private Result getPost(String userId, String postId) {

525

return ok("User: " + userId + ", Post: " + postId);

526

}

527

}

528

```