0
# Request and Response Handling
1
2
Complete HTTP request and response interfaces with entity handling, headers, parameters, and content negotiation for comprehensive HTTP communication.
3
4
## Capabilities
5
6
### Handler Interface
7
8
The core interface for processing HTTP requests and generating responses.
9
10
```java { .api }
11
/**
12
* Interface for handling HTTP requests.
13
*/
14
interface Handler {
15
/**
16
* Handle HTTP request and generate response.
17
* @param req server request
18
* @param res server response
19
*/
20
void handle(ServerRequest req, ServerResponse res);
21
22
/**
23
* Create handler from consumer function.
24
* @param handler consumer that processes the request
25
* @return new handler instance
26
*/
27
static Handler create(Consumer<ServerRequest> handler);
28
29
/**
30
* Create handler from function that returns response data.
31
* @param handler function that processes request and returns response data
32
* @return new handler instance
33
*/
34
static Handler create(Function<ServerRequest, ?> handler);
35
36
/**
37
* Create handler from supplier that returns response data.
38
* @param handler supplier that returns response data
39
* @return new handler instance
40
*/
41
static Handler create(Supplier<?> handler);
42
}
43
```
44
45
**Usage Examples:**
46
47
```java
48
import io.helidon.webserver.http.Handler;
49
50
// Lambda handler
51
Handler lambdaHandler = (req, res) -> {
52
res.send("Hello from lambda");
53
};
54
55
// Consumer-based handler
56
Handler consumerHandler = Handler.create(req -> {
57
System.out.println("Processing request: " + req.method());
58
});
59
60
// Function-based handler
61
Handler functionHandler = Handler.create(req -> {
62
return "Response for " + req.path().path();
63
});
64
65
// Supplier-based handler
66
Handler supplierHandler = Handler.create(() -> {
67
return "Fixed response";
68
});
69
```
70
71
### ServerRequest Interface
72
73
Interface for accessing HTTP request data, headers, parameters, and body content.
74
75
```java { .api }
76
/**
77
* HTTP server request interface.
78
*/
79
interface ServerRequest extends HttpRequest {
80
/**
81
* Get request path information.
82
* @return path information with parameters and matchers
83
*/
84
ServerRequestPath path();
85
86
/**
87
* Get request query parameters.
88
* @return query parameters
89
*/
90
Parameters query();
91
92
/**
93
* Get request headers.
94
* @return request headers
95
*/
96
ServerRequestHeaders headers();
97
98
/**
99
* Get request entity (body).
100
* @return request entity
101
*/
102
ServerRequestEntity entity();
103
104
/**
105
* Get HTTP method.
106
* @return HTTP method
107
*/
108
Method method();
109
110
/**
111
* Get request URI.
112
* @return request URI
113
*/
114
UriInfo requestedUri();
115
116
/**
117
* Get local address of the connection.
118
* @return local address
119
*/
120
String localAddress();
121
122
/**
123
* Get local port of the connection.
124
* @return local port
125
*/
126
int localPort();
127
128
/**
129
* Get remote address of the connection.
130
* @return remote address
131
*/
132
String remoteAddress();
133
134
/**
135
* Get remote port of the connection.
136
* @return remote port
137
*/
138
int remotePort();
139
140
/**
141
* Check if connection is secure (TLS).
142
* @return true if secure connection
143
*/
144
boolean isSecure();
145
146
/**
147
* Get server request context.
148
* @return request context
149
*/
150
Context context();
151
}
152
```
153
154
**Usage Examples:**
155
156
```java
157
Handler requestInfoHandler = (req, res) -> {
158
// Access request information
159
String method = req.method().text();
160
String path = req.path().path();
161
String remoteAddr = req.remoteAddress();
162
boolean secure = req.isSecure();
163
164
// Access path parameters
165
Map<String, String> pathParams = req.path().pathParameters().toMap();
166
String userId = pathParams.get("id");
167
168
// Access query parameters
169
Optional<String> filter = req.query().first("filter");
170
List<String> tags = req.query().all("tag");
171
172
// Access headers
173
Optional<String> contentType = req.headers().contentType()
174
.map(MediaType::text);
175
Optional<String> userAgent = req.headers().first("User-Agent");
176
177
// Build response
178
String info = String.format(
179
"Method: %s, Path: %s, Remote: %s, Secure: %s",
180
method, path, remoteAddr, secure
181
);
182
183
res.send(info);
184
};
185
```
186
187
### ServerResponse Interface
188
189
Interface for generating HTTP responses with status codes, headers, and body content.
190
191
```java { .api }
192
/**
193
* HTTP server response interface.
194
*/
195
interface ServerResponse {
196
/**
197
* Set response status code.
198
* @param status HTTP status
199
* @return this response for chaining
200
*/
201
ServerResponse status(Status status);
202
203
/**
204
* Set response status code.
205
* @param statusCode HTTP status code
206
* @return this response for chaining
207
*/
208
ServerResponse status(int statusCode);
209
210
/**
211
* Add response header.
212
* @param name header name
213
* @param value header value
214
* @return this response for chaining
215
*/
216
ServerResponse header(String name, String value);
217
218
/**
219
* Add response header with multiple values.
220
* @param name header name
221
* @param values header values
222
* @return this response for chaining
223
*/
224
ServerResponse header(String name, String... values);
225
226
/**
227
* Set response headers.
228
* @param headers headers to set
229
* @return this response for chaining
230
*/
231
ServerResponse headers(WritableHeaders<?> headers);
232
233
/**
234
* Set content type.
235
* @param contentType media type
236
* @return this response for chaining
237
*/
238
ServerResponse contentType(MediaType contentType);
239
240
/**
241
* Send response with string content.
242
* @param entity response content
243
*/
244
void send(String entity);
245
246
/**
247
* Send response with byte array content.
248
* @param entity response content
249
*/
250
void send(byte[] entity);
251
252
/**
253
* Send response with input stream content.
254
* @param entity response content
255
*/
256
void send(InputStream entity);
257
258
/**
259
* Send response with object content (uses media support for serialization).
260
* @param entity response object
261
*/
262
void send(Object entity);
263
264
/**
265
* Send empty response.
266
*/
267
void send();
268
269
/**
270
* Re-route request to different path.
271
* @param path new path
272
*/
273
void reroute(String path);
274
275
/**
276
* Re-route request to different path with query parameters.
277
* @param path new path
278
* @param queryParams query parameters
279
*/
280
void reroute(String path, UriQuery queryParams);
281
282
/**
283
* Get response headers for modification.
284
* @return response headers
285
*/
286
ServerResponseHeaders headers();
287
288
/**
289
* Check if response headers have been sent.
290
* @return true if headers sent
291
*/
292
boolean headersSent();
293
294
/**
295
* Get server response context.
296
* @return response context
297
*/
298
Context context();
299
}
300
```
301
302
**Usage Examples:**
303
304
```java
305
import io.helidon.http.Status;
306
import io.helidon.http.MediaType;
307
308
// Basic response handling
309
Handler basicHandler = (req, res) -> {
310
res.status(Status.OK_200)
311
.contentType(MediaType.TEXT_PLAIN)
312
.send("Hello World");
313
};
314
315
// JSON response
316
Handler jsonHandler = (req, res) -> {
317
Map<String, Object> data = Map.of(
318
"message", "Hello",
319
"timestamp", System.currentTimeMillis()
320
);
321
322
res.status(Status.OK_200)
323
.contentType(MediaType.APPLICATION_JSON)
324
.send(data); // Automatically serialized to JSON
325
};
326
327
// Custom headers
328
Handler customHeadersHandler = (req, res) -> {
329
res.status(Status.OK_200)
330
.header("X-Custom-Header", "CustomValue")
331
.header("Cache-Control", "no-cache", "no-store")
332
.contentType(MediaType.TEXT_HTML)
333
.send("<h1>Custom Response</h1>");
334
};
335
336
// File download
337
Handler downloadHandler = (req, res) -> {
338
try {
339
Path filePath = Paths.get("data.pdf");
340
byte[] fileContent = Files.readAllBytes(filePath);
341
342
res.status(Status.OK_200)
343
.contentType(MediaType.APPLICATION_OCTET_STREAM)
344
.header("Content-Disposition", "attachment; filename=data.pdf")
345
.send(fileContent);
346
} catch (IOException e) {
347
res.status(Status.INTERNAL_SERVER_ERROR_500)
348
.send("File not found");
349
}
350
};
351
352
// Streaming response
353
Handler streamHandler = (req, res) -> {
354
try {
355
InputStream stream = new FileInputStream("large-file.txt");
356
res.status(Status.OK_200)
357
.contentType(MediaType.TEXT_PLAIN)
358
.send(stream); // Automatically streams content
359
} catch (FileNotFoundException e) {
360
res.status(Status.NOT_FOUND_404).send("File not found");
361
}
362
};
363
```
364
365
### ServerRequestEntity Interface
366
367
Interface for accessing request body content with various content types.
368
369
```java { .api }
370
/**
371
* Entity part of server request for accessing request body.
372
*/
373
interface ServerRequestEntity {
374
/**
375
* Get entity as string.
376
* @return entity content as string
377
*/
378
String as(Class<String> type);
379
380
/**
381
* Get entity as byte array.
382
* @return entity content as byte array
383
*/
384
byte[] as(Class<byte[]> type);
385
386
/**
387
* Get entity as input stream.
388
* @return entity content as input stream
389
*/
390
InputStream as(Class<InputStream> type);
391
392
/**
393
* Get entity as specific type using media support.
394
* @param type target type
395
* @param <T> type parameter
396
* @return entity converted to target type
397
*/
398
<T> T as(Class<T> type);
399
400
/**
401
* Get entity as generic type using media support.
402
* @param type generic type token
403
* @param <T> type parameter
404
* @return entity converted to target type
405
*/
406
<T> T as(GenericType<T> type);
407
408
/**
409
* Check if entity is available.
410
* @return true if entity is present
411
*/
412
boolean hasEntity();
413
414
/**
415
* Get entity content type.
416
* @return media type of entity
417
*/
418
Optional<MediaType> contentType();
419
420
/**
421
* Get entity content length.
422
* @return content length if known
423
*/
424
OptionalLong contentLength();
425
}
426
```
427
428
**Usage Examples:**
429
430
```java
431
// Handle different content types
432
Handler entityHandler = (req, res) -> {
433
ServerRequestEntity entity = req.entity();
434
435
if (!entity.hasEntity()) {
436
res.status(Status.BAD_REQUEST_400).send("Request body required");
437
return;
438
}
439
440
Optional<MediaType> contentType = entity.contentType();
441
442
if (contentType.isPresent()) {
443
if (MediaType.APPLICATION_JSON.test(contentType.get())) {
444
// Handle JSON
445
Map<String, Object> jsonData = entity.as(Map.class);
446
res.send("Received JSON: " + jsonData);
447
448
} else if (MediaType.TEXT_PLAIN.test(contentType.get())) {
449
// Handle text
450
String textData = entity.as(String.class);
451
res.send("Received text: " + textData);
452
453
} else if (MediaType.APPLICATION_OCTET_STREAM.test(contentType.get())) {
454
// Handle binary data
455
byte[] binaryData = entity.as(byte[].class);
456
res.send("Received " + binaryData.length + " bytes");
457
458
} else {
459
res.status(Status.UNSUPPORTED_MEDIA_TYPE_415)
460
.send("Unsupported content type");
461
}
462
} else {
463
// Default handling
464
String content = entity.as(String.class);
465
res.send("Received: " + content);
466
}
467
};
468
469
// Custom object deserialization
470
Handler userHandler = (req, res) -> {
471
try {
472
User user = req.entity().as(User.class); // Auto-deserialize JSON to User
473
// Process user object
474
res.status(Status.CREATED_201).send("User created: " + user.getName());
475
} catch (Exception e) {
476
res.status(Status.BAD_REQUEST_400).send("Invalid user data");
477
}
478
};
479
```
480
481
### ServerRequestPath Interface
482
483
Interface for accessing path information, parameters, and matching details.
484
485
```java { .api }
486
/**
487
* Server request path information.
488
*/
489
interface ServerRequestPath {
490
/**
491
* Get absolute path of the request.
492
* @return absolute path
493
*/
494
String path();
495
496
/**
497
* Get path parameters extracted from path pattern.
498
* @return path parameters
499
*/
500
Parameters pathParameters();
501
502
/**
503
* Get matched path pattern.
504
* @return path pattern that matched this request
505
*/
506
Optional<String> pathPattern();
507
508
/**
509
* Get path matcher used for matching.
510
* @return path matcher
511
*/
512
Optional<PathMatcher> pathMatcher();
513
514
/**
515
* Get remaining path after matching.
516
* @return remaining unmatched path
517
*/
518
String remainingPath();
519
}
520
```
521
522
### Parameters Interface
523
524
Interface for accessing query and path parameters with type conversion.
525
526
```java { .api }
527
/**
528
* Interface for accessing parameters with type conversion.
529
*/
530
interface Parameters {
531
/**
532
* Get first parameter value.
533
* @param name parameter name
534
* @return optional parameter value
535
*/
536
Optional<String> first(String name);
537
538
/**
539
* Get all parameter values.
540
* @param name parameter name
541
* @return list of parameter values
542
*/
543
List<String> all(String name);
544
545
/**
546
* Get parameter value or default.
547
* @param name parameter name
548
* @param defaultValue default value if parameter not found
549
* @return parameter value or default
550
*/
551
String value(String name, String defaultValue);
552
553
/**
554
* Check if parameter exists.
555
* @param name parameter name
556
* @return true if parameter exists
557
*/
558
boolean contains(String name);
559
560
/**
561
* Get all parameter names.
562
* @return set of parameter names
563
*/
564
Set<String> names();
565
566
/**
567
* Convert to map.
568
* @return map of parameter names to first values
569
*/
570
Map<String, String> toMap();
571
572
/**
573
* Convert to multi-value map.
574
* @return map of parameter names to all values
575
*/
576
Map<String, List<String>> toMultiMap();
577
}
578
```
579
580
**Usage Examples:**
581
582
```java
583
Handler parameterHandler = (req, res) -> {
584
// Path parameters (from /users/{id}/posts/{postId})
585
Parameters pathParams = req.path().pathParameters();
586
String userId = pathParams.first("id").orElse("unknown");
587
String postId = pathParams.first("postId").orElse("unknown");
588
589
// Query parameters (?filter=active&tag=java&tag=web)
590
Parameters queryParams = req.query();
591
String filter = queryParams.value("filter", "all");
592
List<String> tags = queryParams.all("tag");
593
boolean hasSearch = queryParams.contains("search");
594
595
// Convert to maps for easier processing
596
Map<String, String> pathMap = pathParams.toMap();
597
Map<String, List<String>> queryMap = queryParams.toMultiMap();
598
599
String response = String.format(
600
"User: %s, Post: %s, Filter: %s, Tags: %s",
601
userId, postId, filter, tags
602
);
603
604
res.send(response);
605
};
606
```
607
608
## Advanced Request/Response Patterns
609
610
### Content Negotiation
611
612
```java
613
Handler contentNegotiationHandler = (req, res) -> {
614
Optional<String> acceptHeader = req.headers().first("Accept");
615
616
Map<String, Object> data = Map.of(
617
"message", "Hello World",
618
"timestamp", System.currentTimeMillis()
619
);
620
621
if (acceptHeader.isPresent()) {
622
String accept = acceptHeader.get();
623
624
if (accept.contains("application/json")) {
625
res.contentType(MediaType.APPLICATION_JSON).send(data);
626
} else if (accept.contains("application/xml")) {
627
res.contentType(MediaType.APPLICATION_XML).send(toXml(data));
628
} else if (accept.contains("text/html")) {
629
res.contentType(MediaType.TEXT_HTML)
630
.send("<h1>Hello World</h1><p>Timestamp: " + data.get("timestamp") + "</p>");
631
} else {
632
res.contentType(MediaType.TEXT_PLAIN)
633
.send("Hello World at " + data.get("timestamp"));
634
}
635
} else {
636
// Default to JSON
637
res.contentType(MediaType.APPLICATION_JSON).send(data);
638
}
639
};
640
```
641
642
### Request Validation
643
644
```java
645
Handler validationHandler = (req, res) -> {
646
// Validate required headers
647
if (!req.headers().contains("Authorization")) {
648
res.status(Status.UNAUTHORIZED_401)
649
.send("Authorization header required");
650
return;
651
}
652
653
// Validate path parameters
654
String id = req.path().pathParameters().first("id").orElse("");
655
if (id.isEmpty() || !id.matches("\\d+")) {
656
res.status(Status.BAD_REQUEST_400)
657
.send("Invalid ID parameter");
658
return;
659
}
660
661
// Validate query parameters
662
Optional<String> limit = req.query().first("limit");
663
if (limit.isPresent()) {
664
try {
665
int limitValue = Integer.parseInt(limit.get());
666
if (limitValue < 1 || limitValue > 100) {
667
res.status(Status.BAD_REQUEST_400)
668
.send("Limit must be between 1 and 100");
669
return;
670
}
671
} catch (NumberFormatException e) {
672
res.status(Status.BAD_REQUEST_400)
673
.send("Invalid limit parameter");
674
return;
675
}
676
}
677
678
// Process valid request
679
res.send("Valid request processed");
680
};
681
```
682
683
### Response Streaming
684
685
```java
686
Handler streamingHandler = (req, res) -> {
687
res.status(Status.OK_200)
688
.contentType(MediaType.TEXT_PLAIN)
689
.header("Transfer-Encoding", "chunked");
690
691
// Stream large dataset
692
try (BufferedWriter writer = new BufferedWriter(
693
new OutputStreamWriter(res.outputStream()))) {
694
695
for (int i = 0; i < 1000; i++) {
696
writer.write("Data chunk " + i + "\n");
697
writer.flush();
698
699
// Simulate processing delay
700
Thread.sleep(10);
701
}
702
} catch (Exception e) {
703
// Handle streaming errors
704
res.status(Status.INTERNAL_SERVER_ERROR_500)
705
.send("Streaming error");
706
}
707
};
708
```
709
710
### Request Context and State
711
712
```java
713
Handler contextHandler = (req, res) -> {
714
Context requestContext = req.context();
715
716
// Store data in request context
717
requestContext.register("startTime", System.currentTimeMillis());
718
requestContext.register("requestId", UUID.randomUUID().toString());
719
720
// Access context data
721
String requestId = requestContext.get("requestId", String.class)
722
.orElse("unknown");
723
724
res.header("X-Request-ID", requestId)
725
.send("Request processed with ID: " + requestId);
726
};
727
```