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
```