or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

configuration.mdhttp-routing.mdhttp-services.mdhttp1-protocol.mdindex.mdrequest-response.mdserver-management.mdspi.md

http-services.mddocs/

0

# HTTP Services and Features

1

2

Modular service system for creating reusable HTTP components, middleware, and features that can be registered with routing for building scalable web applications.

3

4

## Capabilities

5

6

### HttpService Interface

7

8

Interface for creating reusable HTTP service components that encapsulate related routing logic.

9

10

```java { .api }

11

/**

12

* Interface for HTTP services that can be registered with routing.

13

*/

14

interface HttpService {

15

/**

16

* Configure routing rules for this service.

17

* @param rules routing rules to configure

18

*/

19

void routing(HttpRules rules);

20

}

21

```

22

23

**Usage Examples:**

24

25

```java

26

import io.helidon.webserver.http.HttpService;

27

import io.helidon.webserver.http.HttpRules;

28

29

// User management service

30

class UserService implements HttpService {

31

@Override

32

public void routing(HttpRules rules) {

33

rules.get("/", this::getAllUsers)

34

.get("/{id}", this::getUserById)

35

.post("/", this::createUser)

36

.put("/{id}", this::updateUser)

37

.delete("/{id}", this::deleteUser);

38

}

39

40

private void getAllUsers(ServerRequest req, ServerResponse res) {

41

// Implementation for getting all users

42

res.send("All users");

43

}

44

45

private void getUserById(ServerRequest req, ServerResponse res) {

46

String id = req.path().pathParameters().get("id");

47

res.send("User: " + id);

48

}

49

50

private void createUser(ServerRequest req, ServerResponse res) {

51

// Implementation for creating user

52

res.status(201).send("User created");

53

}

54

55

private void updateUser(ServerRequest req, ServerResponse res) {

56

String id = req.path().pathParameters().get("id");

57

res.send("User " + id + " updated");

58

}

59

60

private void deleteUser(ServerRequest req, ServerResponse res) {

61

String id = req.path().pathParameters().get("id");

62

res.status(204).send();

63

}

64

}

65

66

// Register service with routing

67

HttpService userService = new UserService();

68

HttpRouting routing = HttpRouting.builder()

69

.register("/api/users", userService)

70

.build();

71

```

72

73

### HttpFeature Interface

74

75

Interface for creating HTTP features that provide cross-cutting functionality across multiple routes.

76

77

```java { .api }

78

/**

79

* Interface for HTTP features that enhance routing functionality.

80

*/

81

interface HttpFeature {

82

/**

83

* Setup feature with routing builder.

84

* @param routing routing builder to configure

85

*/

86

void setup(HttpRouting.Builder routing);

87

}

88

```

89

90

**Usage Examples:**

91

92

```java

93

import io.helidon.webserver.http.HttpFeature;

94

95

// CORS feature

96

class CorsFeature implements HttpFeature {

97

private final String allowedOrigins;

98

private final String allowedMethods;

99

100

public CorsFeature(String allowedOrigins, String allowedMethods) {

101

this.allowedOrigins = allowedOrigins;

102

this.allowedMethods = allowedMethods;

103

}

104

105

@Override

106

public void setup(HttpRouting.Builder routing) {

107

// Add CORS filter

108

routing.addFilter((chain, req, res) -> {

109

res.header("Access-Control-Allow-Origin", allowedOrigins)

110

.header("Access-Control-Allow-Methods", allowedMethods)

111

.header("Access-Control-Allow-Headers", "Content-Type, Authorization");

112

113

if ("OPTIONS".equals(req.method().text())) {

114

res.status(200).send();

115

} else {

116

chain.proceed();

117

}

118

});

119

}

120

}

121

122

// Logging feature

123

class LoggingFeature implements HttpFeature {

124

@Override

125

public void setup(HttpRouting.Builder routing) {

126

routing.addFilter((chain, req, res) -> {

127

long startTime = System.currentTimeMillis();

128

System.out.println("Request: " + req.method() + " " + req.path().path());

129

130

chain.proceed();

131

132

long duration = System.currentTimeMillis() - startTime;

133

System.out.println("Response time: " + duration + "ms");

134

});

135

}

136

}

137

138

// Use features in routing

139

HttpRouting routing = HttpRouting.builder()

140

.addFeature(new CorsFeature("*", "GET,POST,PUT,DELETE"))

141

.addFeature(new LoggingFeature())

142

.get("/api/data", (req, res) -> res.send("Data"))

143

.build();

144

```

145

146

### Filter Interface

147

148

Interface for creating HTTP filters that provide request/response processing middleware.

149

150

```java { .api }

151

/**

152

* Interface for HTTP filters that process requests and responses.

153

*/

154

interface Filter {

155

/**

156

* Filter request and response.

157

* @param chain filter chain to continue processing

158

* @param req routing request

159

* @param res routing response

160

*/

161

void filter(FilterChain chain, RoutingRequest req, RoutingResponse res);

162

}

163

```

164

165

### FilterChain Interface

166

167

Interface for controlling filter chain execution.

168

169

```java { .api }

170

/**

171

* Chain of filters for request processing.

172

*/

173

interface FilterChain {

174

/**

175

* Continue to next filter or handler in the chain.

176

*/

177

void proceed();

178

}

179

```

180

181

**Usage Examples:**

182

183

```java

184

import io.helidon.webserver.http.Filter;

185

import io.helidon.webserver.http.FilterChain;

186

187

// Authentication filter

188

Filter authFilter = (chain, req, res) -> {

189

Optional<String> authHeader = req.headers().first("Authorization");

190

191

if (authHeader.isPresent() && authHeader.get().startsWith("Bearer ")) {

192

String token = authHeader.get().substring(7);

193

194

if (isValidToken(token)) {

195

// Add user info to context

196

req.context().register("userId", extractUserId(token));

197

chain.proceed(); // Continue to next filter/handler

198

} else {

199

res.status(401).send("Invalid token");

200

}

201

} else {

202

res.status(401).send("Authorization required");

203

}

204

};

205

206

// Request timing filter

207

Filter timingFilter = (chain, req, res) -> {

208

long startTime = System.currentTimeMillis();

209

210

chain.proceed();

211

212

long duration = System.currentTimeMillis() - startTime;

213

res.header("X-Response-Time", duration + "ms");

214

};

215

216

// Rate limiting filter

217

Filter rateLimitFilter = (chain, req, res) -> {

218

String clientIp = req.remoteAddress();

219

220

if (isRateLimited(clientIp)) {

221

res.status(429).send("Rate limit exceeded");

222

} else {

223

recordRequest(clientIp);

224

chain.proceed();

225

}

226

};

227

228

// Register filters

229

HttpRouting routing = HttpRouting.builder()

230

.addFilter(rateLimitFilter) // Applied first

231

.addFilter(authFilter) // Applied second

232

.addFilter(timingFilter) // Applied third

233

.get("/api/secure", (req, res) -> {

234

String userId = req.context().get("userId", String.class).orElse("unknown");

235

res.send("Secure data for user: " + userId);

236

})

237

.build();

238

```

239

240

### ErrorHandler Interface

241

242

Interface for handling exceptions thrown during request processing.

243

244

```java { .api }

245

/**

246

* Interface for handling exceptions of specific types.

247

* @param <T> exception type

248

*/

249

interface ErrorHandler<T extends Throwable> {

250

/**

251

* Handle exception and generate error response.

252

* @param req server request

253

* @param res server response

254

* @param throwable the exception to handle

255

*/

256

void handle(ServerRequest req, ServerResponse res, T throwable);

257

}

258

```

259

260

**Usage Examples:**

261

262

```java

263

import io.helidon.webserver.http.ErrorHandler;

264

265

// Validation error handler

266

ErrorHandler<IllegalArgumentException> validationErrorHandler =

267

(req, res, ex) -> {

268

res.status(400)

269

.contentType(MediaType.APPLICATION_JSON)

270

.send(Map.of(

271

"error", "Validation Error",

272

"message", ex.getMessage(),

273

"timestamp", System.currentTimeMillis()

274

));

275

};

276

277

// Database error handler

278

ErrorHandler<SQLException> databaseErrorHandler =

279

(req, res, ex) -> {

280

System.err.println("Database error: " + ex.getMessage());

281

res.status(500)

282

.send("Database temporarily unavailable");

283

};

284

285

// Security error handler

286

ErrorHandler<SecurityException> securityErrorHandler =

287

(req, res, ex) -> {

288

String requestId = UUID.randomUUID().toString();

289

System.err.println("Security violation [" + requestId + "]: " + ex.getMessage());

290

291

res.status(403)

292

.header("X-Request-ID", requestId)

293

.send("Access denied");

294

};

295

296

// Generic error handler

297

ErrorHandler<Exception> genericErrorHandler =

298

(req, res, ex) -> {

299

String requestId = UUID.randomUUID().toString();

300

System.err.println("Unexpected error [" + requestId + "]: " + ex.getMessage());

301

302

res.status(500)

303

.header("X-Request-ID", requestId)

304

.send("Internal server error");

305

};

306

307

// Register error handlers

308

HttpRouting routing = HttpRouting.builder()

309

.error(IllegalArgumentException.class, validationErrorHandler)

310

.error(SQLException.class, databaseErrorHandler)

311

.error(SecurityException.class, securityErrorHandler)

312

.error(Exception.class, genericErrorHandler) // Catch-all

313

314

.get("/api/users/{id}", (req, res) -> {

315

String id = req.path().pathParameters().get("id");

316

317

// Validation that might throw IllegalArgumentException

318

if (!id.matches("\\d+")) {

319

throw new IllegalArgumentException("ID must be numeric");

320

}

321

322

// Database operation that might throw SQLException

323

User user = userRepository.findById(Long.parseLong(id));

324

res.send(user);

325

})

326

.build();

327

```

328

329

### Service Registration Patterns

330

331

Multiple approaches for registering HTTP services and organizing application logic.

332

333

```java { .api }

334

/**

335

* Service registration methods in HttpRouting.Builder

336

*/

337

interface Builder {

338

/**

339

* Register services without path prefix.

340

* @param service services to register

341

* @return updated builder

342

*/

343

Builder register(HttpService... service);

344

345

/**

346

* Register services with path prefix.

347

* @param path path prefix for all service routes

348

* @param service services to register

349

* @return updated builder

350

*/

351

Builder register(String path, HttpService... service);

352

353

/**

354

* Register service suppliers for lazy initialization.

355

* @param service service supplier

356

* @return updated builder

357

*/

358

default Builder register(Supplier<? extends HttpService> service);

359

360

/**

361

* Register multiple service suppliers.

362

* @param services list of service suppliers

363

* @return updated builder

364

*/

365

default Builder register(List<Supplier<? extends HttpService>> services);

366

}

367

```

368

369

**Usage Examples:**

370

371

```java

372

// Multiple service classes

373

class UserService implements HttpService {

374

@Override

375

public void routing(HttpRules rules) {

376

rules.get("/", (req, res) -> res.send("Users"))

377

.get("/{id}", (req, res) -> res.send("User details"));

378

}

379

}

380

381

class OrderService implements HttpService {

382

@Override

383

public void routing(HttpRules rules) {

384

rules.get("/", (req, res) -> res.send("Orders"))

385

.post("/", (req, res) -> res.status(201).send("Order created"));

386

}

387

}

388

389

class ProductService implements HttpService {

390

@Override

391

public void routing(HttpRules rules) {

392

rules.get("/", (req, res) -> res.send("Products"))

393

.get("/{id}", (req, res) -> res.send("Product details"));

394

}

395

}

396

397

// Register services with different approaches

398

HttpRouting routing = HttpRouting.builder()

399

// Direct registration with path prefixes

400

.register("/api/users", new UserService())

401

.register("/api/orders", new OrderService())

402

.register("/api/products", new ProductService())

403

404

// Multiple services with same prefix

405

.register("/admin", new UserService(), new OrderService())

406

407

// Lazy service registration

408

.register(() -> new UserService())

409

.register("/api/v2/users", () -> new UserService())

410

411

// Service list registration

412

.register(Arrays.asList(

413

() -> new UserService(),

414

() -> new OrderService(),

415

() -> new ProductService()

416

))

417

418

.build();

419

```

420

421

## Advanced Service Patterns

422

423

### Service Composition

424

425

```java

426

// Base service with common functionality

427

abstract class BaseService implements HttpService {

428

protected void addCommonRoutes(HttpRules rules) {

429

rules.get("/health", (req, res) -> res.send("OK"))

430

.get("/version", (req, res) -> res.send("1.0.0"));

431

}

432

}

433

434

// Specific services extending base functionality

435

class UserService extends BaseService {

436

@Override

437

public void routing(HttpRules rules) {

438

addCommonRoutes(rules);

439

440

rules.get("/", this::getAllUsers)

441

.get("/{id}", this::getUserById)

442

.post("/", this::createUser);

443

}

444

445

// Implementation methods...

446

}

447

448

// Service with dependency injection

449

class OrderService implements HttpService {

450

private final OrderRepository orderRepository;

451

private final NotificationService notificationService;

452

453

public OrderService(OrderRepository orderRepository,

454

NotificationService notificationService) {

455

this.orderRepository = orderRepository;

456

this.notificationService = notificationService;

457

}

458

459

@Override

460

public void routing(HttpRules rules) {

461

rules.post("/", this::createOrder)

462

.get("/{id}", this::getOrder)

463

.put("/{id}/status", this::updateOrderStatus);

464

}

465

466

private void createOrder(ServerRequest req, ServerResponse res) {

467

Order order = req.entity().as(Order.class);

468

Order saved = orderRepository.save(order);

469

notificationService.sendOrderConfirmation(saved);

470

res.status(201).send(saved);

471

}

472

}

473

```

474

475

### Feature Composition

476

477

```java

478

// Composable feature for API versioning

479

class ApiVersionFeature implements HttpFeature {

480

private final String version;

481

482

public ApiVersionFeature(String version) {

483

this.version = version;

484

}

485

486

@Override

487

public void setup(HttpRouting.Builder routing) {

488

routing.addFilter((chain, req, res) -> {

489

res.header("API-Version", version);

490

chain.proceed();

491

});

492

}

493

}

494

495

// Security feature with role-based access

496

class SecurityFeature implements HttpFeature {

497

private final SecurityManager securityManager;

498

499

public SecurityFeature(SecurityManager securityManager) {

500

this.securityManager = securityManager;

501

}

502

503

@Override

504

public void setup(HttpRouting.Builder routing) {

505

routing.addFilter((chain, req, res) -> {

506

if (requiresAuthentication(req)) {

507

if (securityManager.authenticate(req)) {

508

chain.proceed();

509

} else {

510

res.status(401).send("Unauthorized");

511

}

512

} else {

513

chain.proceed();

514

}

515

});

516

}

517

}

518

519

// Metrics feature

520

class MetricsFeature implements HttpFeature {

521

private final MeterRegistry meterRegistry;

522

523

public MetricsFeature(MeterRegistry meterRegistry) {

524

this.meterRegistry = meterRegistry;

525

}

526

527

@Override

528

public void setup(HttpRouting.Builder routing) {

529

routing.addFilter((chain, req, res) -> {

530

Timer.Sample sample = Timer.start(meterRegistry);

531

532

try {

533

chain.proceed();

534

} finally {

535

sample.stop(Timer.builder("http.requests")

536

.tag("method", req.method().text())

537

.tag("path", req.path().path())

538

.register(meterRegistry));

539

}

540

});

541

}

542

}

543

544

// Combine features

545

HttpRouting routing = HttpRouting.builder()

546

.addFeature(new ApiVersionFeature("v1"))

547

.addFeature(new SecurityFeature(securityManager))

548

.addFeature(new MetricsFeature(meterRegistry))

549

.register("/api/users", userService)

550

.register("/api/orders", orderService)

551

.build();

552

```

553

554

### Service Factory Pattern

555

556

```java

557

// Service factory for creating configured services

558

class ServiceFactory {

559

private final DataSource dataSource;

560

private final CacheManager cacheManager;

561

562

public ServiceFactory(DataSource dataSource, CacheManager cacheManager) {

563

this.dataSource = dataSource;

564

this.cacheManager = cacheManager;

565

}

566

567

public UserService createUserService() {

568

return new UserService(

569

new UserRepository(dataSource),

570

new UserCache(cacheManager)

571

);

572

}

573

574

public OrderService createOrderService() {

575

return new OrderService(

576

new OrderRepository(dataSource),

577

new NotificationService()

578

);

579

}

580

}

581

582

// Configuration-driven service registration

583

class ApplicationConfig {

584

public HttpRouting createRouting() {

585

ServiceFactory factory = new ServiceFactory(dataSource, cacheManager);

586

587

return HttpRouting.builder()

588

.addFeature(new CorsFeature("*", "GET,POST,PUT,DELETE"))

589

.addFeature(new LoggingFeature())

590

591

.register("/api/v1/users", factory::createUserService)

592

.register("/api/v1/orders", factory::createOrderService)

593

594

.error(ValidationException.class, this::handleValidationError)

595

.error(Exception.class, this::handleGenericError)

596

597

.build();

598

}

599

}

600

```