0
# Request and Response Handling
1
2
Comprehensive request parsing and response building capabilities with content transformation pipelines. Provides full access to HTTP request data and flexible response generation with content negotiation support.
3
4
## Capabilities
5
6
### Application Request Interface
7
8
Core interface for accessing incoming HTTP request data and metadata.
9
10
```kotlin { .api }
11
/**
12
* Represents client request with all HTTP data and metadata
13
*/
14
interface ApplicationRequest {
15
/** The call this request belongs to */
16
val call: ApplicationCall
17
18
/** Pipeline for processing request content */
19
val pipeline: ApplicationReceivePipeline
20
21
/** Query string parameters */
22
val queryParameters: Parameters
23
24
/** HTTP request headers */
25
val headers: Headers
26
27
/** Local connection information */
28
val local: RequestConnectionPoint
29
30
/** Request cookies accessor */
31
val cookies: RequestCookies
32
}
33
34
/**
35
* Pipeline-specific request implementation
36
*/
37
interface PipelineRequest : ApplicationRequest
38
```
39
40
### Request Connection Information
41
42
Interfaces for accessing connection and HTTP protocol information.
43
44
```kotlin { .api }
45
/**
46
* Connection information for incoming request
47
*/
48
interface RequestConnectionPoint {
49
/** URI scheme (http, https) */
50
val scheme: String
51
52
/** HTTP protocol version */
53
val version: String
54
55
/** Local port number */
56
val port: Int
57
58
/** Host name from Host header */
59
val host: String
60
61
/** Full request URI */
62
val uri: String
63
64
/** HTTP method */
65
val method: HttpMethod
66
67
/** Remote client host address */
68
val remoteHost: String
69
70
/** User-Agent header value */
71
val userAgent: String?
72
}
73
```
74
75
### Request Cookies
76
77
Interface for accessing HTTP cookies from the request.
78
79
```kotlin { .api }
80
/**
81
* Request cookies accessor
82
*/
83
interface RequestCookies {
84
/** Get cookie value by name */
85
operator fun get(name: String): String?
86
87
/** Get all cookies as a map */
88
fun getAll(): Map<String, String>
89
}
90
```
91
92
### Request Content Pipeline
93
94
Pipeline system for processing and transforming incoming request content.
95
96
```kotlin { .api }
97
/**
98
* Pipeline for processing incoming request content
99
*/
100
open class ApplicationReceivePipeline : Pipeline<Any, PipelineCall> {
101
companion object {
102
/** Phase for pre-processing before transformation */
103
val Before: PipelinePhase
104
105
/** Phase for content transformation */
106
val Transform: PipelinePhase
107
108
/** Phase for post-processing after transformation */
109
val After: PipelinePhase
110
}
111
}
112
```
113
114
### Request Extension Properties
115
116
Extension properties providing convenient access to common request data.
117
118
```kotlin { .api }
119
/** Full request URI */
120
val ApplicationRequest.uri: String
121
122
/** HTTP protocol version */
123
val ApplicationRequest.httpVersion: String
124
125
/** HTTP method */
126
val ApplicationRequest.httpMethod: HttpMethod
127
128
/** Request path without query parameters */
129
val ApplicationRequest.path: String
130
131
/** Document name from path */
132
val ApplicationRequest.document: String
133
134
/** Host header value */
135
val ApplicationRequest.host: String
136
137
/** Port number */
138
val ApplicationRequest.port: Int
139
140
/** User-Agent header */
141
val ApplicationRequest.userAgent: String?
142
143
/** Accept header */
144
val ApplicationRequest.accept: String?
145
146
/** Accept-Language header */
147
val ApplicationRequest.acceptLanguage: String?
148
149
/** Accept-Encoding header */
150
val ApplicationRequest.acceptEncoding: String?
151
152
/** Content-Type header parsed */
153
val ApplicationRequest.contentType: ContentType
154
155
/** Content-Length header */
156
val ApplicationRequest.contentLength: Long?
157
158
/** Authorization header */
159
val ApplicationRequest.authorization: String?
160
161
/** Cache-Control header */
162
val ApplicationRequest.cacheControl: String?
163
164
/** Whether request is multipart */
165
val ApplicationRequest.isMultipart: Boolean
166
167
/** Whether request is URL encoded */
168
val ApplicationRequest.isUrlEncoded: Boolean
169
170
/** Origin connection point */
171
val ApplicationRequest.origin: RequestConnectionPoint
172
```
173
174
### Request Receive Functions
175
176
Extension functions for receiving and parsing request content with type safety.
177
178
```kotlin { .api }
179
/**
180
* Receive request body converted to specified type
181
*/
182
suspend inline fun <reified T> ApplicationCall.receive(): T
183
184
/**
185
* Receive request body converted to specified type or null if conversion fails
186
*/
187
suspend inline fun <reified T> ApplicationCall.receiveOrNull(): T?
188
189
/**
190
* Receive request body with explicit type information
191
*/
192
suspend fun <T> ApplicationCall.receiveNullable(typeInfo: TypeInfo): T?
193
194
/**
195
* Receive request body as plain text
196
*/
197
suspend fun ApplicationCall.receiveText(): String
198
199
/**
200
* Receive form parameters from URL-encoded or multipart request
201
*/
202
suspend fun ApplicationCall.receiveParameters(): Parameters
203
204
/**
205
* Receive multipart data for file uploads and complex forms
206
*/
207
suspend fun ApplicationCall.receiveMultipart(): MultiPartData
208
209
/**
210
* Receive request body as byte array
211
*/
212
suspend fun ApplicationCall.receiveBytes(): ByteArray
213
214
/**
215
* Receive request body as input stream
216
*/
217
suspend fun ApplicationCall.receiveStream(): InputStream
218
```
219
220
### Application Response Interface
221
222
Core interface for building and sending HTTP responses.
223
224
```kotlin { .api }
225
/**
226
* Represents server response being built and sent to client
227
*/
228
interface ApplicationResponse {
229
/** The call this response belongs to */
230
val call: ApplicationCall
231
232
/** Pipeline for processing response content */
233
val pipeline: ApplicationSendPipeline
234
235
/** Response headers accessor */
236
val headers: ResponseHeaders
237
238
/** Response cookies accessor */
239
val cookies: ResponseCookies
240
241
/** Get current response status code */
242
fun status(): HttpStatusCode?
243
244
/** Set response status code */
245
fun status(value: HttpStatusCode)
246
}
247
248
/**
249
* Pipeline-specific response implementation
250
*/
251
interface PipelineResponse : ApplicationResponse
252
```
253
254
### Response Headers Management
255
256
Interface for managing HTTP response headers.
257
258
```kotlin { .api }
259
/**
260
* Response headers accessor with modification capabilities
261
*/
262
interface ResponseHeaders {
263
/** Get header value by name */
264
operator fun get(name: String): String?
265
266
/** Get all values for header name */
267
fun getAll(name: String): List<String>?
268
269
/** Check if header exists */
270
fun contains(name: String): Boolean
271
272
/** Add header value (allows multiple values) */
273
fun append(name: String, value: String)
274
275
/** Add header value only if name doesn't exist */
276
fun appendIfNameAbsent(name: String, value: String): Boolean
277
}
278
```
279
280
### Response Cookies Management
281
282
Interface for managing HTTP response cookies.
283
284
```kotlin { .api }
285
/**
286
* Response cookies accessor for setting cookies
287
*/
288
interface ResponseCookies {
289
/** Get cookie configuration by name */
290
operator fun get(name: String): Cookie?
291
292
/** Add cookie to response */
293
fun append(cookie: Cookie)
294
295
/** Add expired cookie to delete client-side cookie */
296
fun appendExpired(name: String, domain: String? = null, path: String? = null)
297
}
298
```
299
300
### Response Content Pipeline
301
302
Pipeline system for processing and transforming outgoing response content.
303
304
```kotlin { .api }
305
/**
306
* Pipeline for processing outgoing response content
307
*/
308
open class ApplicationSendPipeline : Pipeline<Any, PipelineCall> {
309
companion object {
310
/** Phase for pre-processing before transformation */
311
val Before: PipelinePhase
312
313
/** Phase for content transformation */
314
val Transform: PipelinePhase
315
316
/** Phase for post-processing after transformation */
317
val After: PipelinePhase
318
319
/** Phase for engine-specific processing */
320
val Engine: PipelinePhase
321
}
322
}
323
```
324
325
### Request Extension Properties
326
327
Extension properties for convenient request data access.
328
329
```kotlin { .api }
330
/** Get request URI */
331
val ApplicationRequest.uri: String
332
333
/** Get HTTP method */
334
val ApplicationRequest.httpMethod: HttpMethod
335
336
/** Get request path */
337
val ApplicationRequest.path: String
338
339
/** Get request host */
340
val ApplicationRequest.host: String
341
342
/** Get request port */
343
val ApplicationRequest.port: Int
344
345
/** Get Content-Type header */
346
val ApplicationRequest.contentType: ContentType
347
348
/** Get User-Agent header */
349
val ApplicationRequest.userAgent: String?
350
351
/** Get request origin information */
352
val ApplicationRequest.origin: RequestConnectionPoint
353
```
354
355
### Response Extension Properties
356
357
Extension properties for convenient response header management.
358
359
```kotlin { .api }
360
/** Get or set Content-Type header */
361
var ApplicationResponse.contentType: ContentType?
362
363
/** Get or set Content-Length header */
364
var ApplicationResponse.contentLength: Long?
365
366
/** Get or set Cache-Control header */
367
var ApplicationResponse.cacheControl: CacheControl?
368
369
/** Get or set ETag header */
370
var ApplicationResponse.etag: String?
371
372
/** Get or set Last-Modified header */
373
var ApplicationResponse.lastModified: ZonedDateTime?
374
```
375
376
### Response Send Functions
377
378
Extension functions for sending various types of response content.
379
380
```kotlin { .api }
381
/**
382
* Send response content with automatic type handling
383
*/
384
suspend fun ApplicationCall.respond(message: Any)
385
386
/**
387
* Send response with specific status code
388
*/
389
suspend fun ApplicationCall.respond(status: HttpStatusCode, message: Any)
390
391
/**
392
* Send plain text response
393
*/
394
suspend fun ApplicationCall.respondText(
395
text: String,
396
contentType: ContentType? = null,
397
status: HttpStatusCode? = null
398
)
399
400
/**
401
* Send byte array response
402
*/
403
suspend fun ApplicationCall.respondBytes(
404
bytes: ByteArray,
405
contentType: ContentType? = null,
406
status: HttpStatusCode? = null
407
)
408
409
/**
410
* Send file response with optional content type detection
411
*/
412
suspend fun ApplicationCall.respondFile(
413
file: File,
414
contentType: ContentType? = null
415
)
416
417
/**
418
* Send redirect response
419
*/
420
suspend fun ApplicationCall.respondRedirect(
421
url: String,
422
permanent: Boolean = false
423
)
424
425
/**
426
* Send redirect response with status code
427
*/
428
suspend fun ApplicationCall.respondRedirect(
429
url: String,
430
status: HttpStatusCode
431
)
432
433
/**
434
* Send streaming response from input stream
435
*/
436
suspend fun ApplicationCall.respondOutputStream(
437
contentType: ContentType? = null,
438
status: HttpStatusCode? = null,
439
producer: suspend OutputStream.() -> Unit
440
)
441
442
/**
443
* Send streaming response from writer
444
*/
445
suspend fun ApplicationCall.respondTextWriter(
446
contentType: ContentType? = null,
447
status: HttpStatusCode? = null,
448
producer: suspend Writer.() -> Unit
449
)
450
```
451
452
## Usage Examples
453
454
### Request Data Access
455
456
```kotlin
457
import io.ktor.server.application.*
458
import io.ktor.server.request.*
459
import io.ktor.server.response.*
460
import io.ktor.server.routing.*
461
462
fun Application.requestHandling() {
463
routing {
464
get("/request-info") {
465
val request = call.request
466
467
val info = mapOf(
468
"method" to request.httpMethod.value,
469
"uri" to request.uri,
470
"path" to request.path,
471
"host" to request.host,
472
"port" to request.port,
473
"userAgent" to request.userAgent,
474
"contentType" to request.contentType.toString(),
475
"accept" to request.accept,
476
"headers" to request.headers.toMap(),
477
"queryParams" to request.queryParameters.toMap(),
478
"cookies" to request.cookies.getAll()
479
)
480
481
call.respond(info)
482
}
483
484
get("/users/{id}") {
485
val userId = call.parameters["id"]
486
val includeDetails = call.request.queryParameters["details"]?.toBoolean() ?: false
487
488
val user = getUserById(userId)
489
if (includeDetails) {
490
call.respond(user.withDetails())
491
} else {
492
call.respond(user.summary())
493
}
494
}
495
}
496
}
497
```
498
499
### Request Content Handling
500
501
```kotlin
502
import io.ktor.server.application.*
503
import io.ktor.server.request.*
504
import io.ktor.server.response.*
505
import io.ktor.server.routing.*
506
507
fun Application.contentHandling() {
508
routing {
509
// Receive JSON data
510
post("/users") {
511
try {
512
val user = call.receive<User>()
513
val savedUser = userService.createUser(user)
514
call.respond(HttpStatusCode.Created, savedUser)
515
} catch (e: ContentTransformationException) {
516
call.respond(HttpStatusCode.BadRequest, "Invalid user data")
517
}
518
}
519
520
// Receive form data
521
post("/form") {
522
val formParameters = call.receiveParameters()
523
val name = formParameters["name"] ?: return@post call.respond(HttpStatusCode.BadRequest)
524
val email = formParameters["email"] ?: return@post call.respond(HttpStatusCode.BadRequest)
525
526
call.respond("Hello $name, your email is $email")
527
}
528
529
// Receive multipart data (file upload)
530
post("/upload") {
531
val multipart = call.receiveMultipart()
532
var fileName: String? = null
533
var fileBytes: ByteArray? = null
534
535
multipart.forEachPart { part ->
536
when (part) {
537
is PartData.FormItem -> {
538
if (part.name == "description") {
539
// Handle form field
540
}
541
}
542
is PartData.FileItem -> {
543
fileName = part.originalFileName
544
fileBytes = part.streamProvider().readBytes()
545
}
546
is PartData.BinaryItem -> {
547
// Handle binary data
548
}
549
}
550
part.dispose()
551
}
552
553
if (fileName != null && fileBytes != null) {
554
// Save file
555
call.respond("File $fileName uploaded successfully")
556
} else {
557
call.respond(HttpStatusCode.BadRequest, "No file provided")
558
}
559
}
560
561
// Receive raw text
562
put("/data/{id}") {
563
val id = call.parameters["id"]
564
val textData = call.receiveText()
565
566
dataService.updateData(id, textData)
567
call.respond(HttpStatusCode.OK)
568
}
569
}
570
}
571
```
572
573
### Response Building
574
575
```kotlin
576
import io.ktor.server.application.*
577
import io.ktor.server.response.*
578
import io.ktor.server.routing.*
579
580
fun Application.responseHandling() {
581
routing {
582
// JSON response
583
get("/api/users/{id}") {
584
val userId = call.parameters["id"]
585
val user = userService.findById(userId)
586
587
if (user != null) {
588
call.respond(user)
589
} else {
590
call.respond(HttpStatusCode.NotFound, "User not found")
591
}
592
}
593
594
// Custom headers and status
595
get("/api/data") {
596
val data = dataService.getData()
597
598
call.response.headers.append("X-Data-Version", "1.0")
599
call.response.headers.append("X-Total-Count", data.size.toString())
600
call.response.cacheControl = CacheControl.MaxAge(maxAgeSeconds = 300)
601
602
call.respond(HttpStatusCode.OK, data)
603
}
604
605
// File response
606
get("/files/{filename}") {
607
val filename = call.parameters["filename"]
608
val file = File("uploads/$filename")
609
610
if (file.exists() && file.isFile) {
611
call.respondFile(file)
612
} else {
613
call.respond(HttpStatusCode.NotFound)
614
}
615
}
616
617
// Streaming response
618
get("/large-data") {
619
call.respondTextWriter(ContentType.Text.Plain) {
620
for (i in 1..1000000) {
621
write("Line $i\n")
622
if (i % 1000 == 0) {
623
flush() // Flush periodically for streaming
624
}
625
}
626
}
627
}
628
629
// Binary response
630
get("/image/{id}") {
631
val imageId = call.parameters["id"]
632
val imageData = imageService.getImageBytes(imageId)
633
634
if (imageData != null) {
635
call.respondBytes(
636
bytes = imageData,
637
contentType = ContentType.Image.PNG
638
)
639
} else {
640
call.respond(HttpStatusCode.NotFound)
641
}
642
}
643
644
// Redirect response
645
get("/old-api/{path...}") {
646
val path = call.parameters.getAll("path")?.joinToString("/") ?: ""
647
call.respondRedirect("/api/v2/$path", permanent = true)
648
}
649
}
650
}
651
```
652
653
### Cookie Management
654
655
```kotlin
656
import io.ktor.server.application.*
657
import io.ktor.server.request.*
658
import io.ktor.server.response.*
659
import io.ktor.server.routing.*
660
661
fun Application.cookieHandling() {
662
routing {
663
get("/set-cookie") {
664
// Set simple cookie
665
call.response.cookies.append("session_id", "abc123")
666
667
// Set cookie with options
668
call.response.cookies.append(Cookie(
669
name = "user_pref",
670
value = "dark_mode",
671
maxAge = 86400, // 24 hours
672
domain = ".example.com",
673
path = "/",
674
secure = true,
675
httpOnly = true,
676
extensions = mapOf("SameSite" to "Strict")
677
))
678
679
call.respond("Cookies set")
680
}
681
682
get("/get-cookies") {
683
val sessionId = call.request.cookies["session_id"]
684
val userPref = call.request.cookies["user_pref"]
685
val allCookies = call.request.cookies.getAll()
686
687
call.respond(mapOf(
688
"sessionId" to sessionId,
689
"userPref" to userPref,
690
"allCookies" to allCookies
691
))
692
}
693
694
post("/logout") {
695
// Delete cookies by setting expired ones
696
call.response.cookies.appendExpired("session_id")
697
call.response.cookies.appendExpired("user_pref", domain = ".example.com", path = "/")
698
699
call.respond("Logged out")
700
}
701
}
702
}
703
```
704
705
### Content Negotiation and Type Conversion
706
707
```kotlin
708
import io.ktor.server.application.*
709
import io.ktor.server.request.*
710
import io.ktor.server.response.*
711
import io.ktor.server.routing.*
712
713
data class User(val id: Int, val name: String, val email: String)
714
data class CreateUserRequest(val name: String, val email: String)
715
716
fun Application.contentNegotiation() {
717
routing {
718
// Accept both JSON and XML
719
post("/users") {
720
val contentType = call.request.contentType
721
722
when {
723
contentType.match(ContentType.Application.Json) -> {
724
val request = call.receive<CreateUserRequest>()
725
val user = userService.create(request)
726
call.respond(user)
727
}
728
contentType.match(ContentType.Application.Xml) -> {
729
val xmlData = call.receiveText()
730
val user = parseUserFromXml(xmlData)
731
call.respond(user)
732
}
733
else -> {
734
call.respond(HttpStatusCode.UnsupportedMediaType)
735
}
736
}
737
}
738
739
// Content negotiation for response format
740
get("/users/{id}") {
741
val userId = call.parameters["id"]?.toInt()
742
val user = userService.findById(userId)
743
744
if (user == null) {
745
call.respond(HttpStatusCode.NotFound)
746
return@get
747
}
748
749
val acceptHeader = call.request.accept
750
when {
751
acceptHeader?.contains("application/json") == true -> {
752
call.respond(user)
753
}
754
acceptHeader?.contains("text/xml") == true -> {
755
call.respondText(
756
user.toXml(),
757
ContentType.Text.Xml
758
)
759
}
760
acceptHeader?.contains("text/plain") == true -> {
761
call.respondText("User: ${user.name} (${user.email})")
762
}
763
else -> {
764
call.respond(user) // Default to JSON
765
}
766
}
767
}
768
}
769
}
770
```
771
772
### Exception Classes
773
774
Exception types related to request/response processing and content transformation.
775
776
```kotlin { .api }
777
/**
778
* Base exception for bad request scenarios
779
*/
780
open class BadRequestException(message: String, cause: Throwable? = null) : Exception(message, cause)
781
782
/**
783
* Thrown when a required request parameter is missing
784
*/
785
class MissingRequestParameterException(val parameterName: String) : BadRequestException(
786
"Request parameter '$parameterName' is missing"
787
)
788
789
/**
790
* Thrown when parameter conversion fails
791
*/
792
class ParameterConversionException(
793
val parameterName: String,
794
val type: String,
795
cause: Throwable? = null
796
) : BadRequestException("Cannot convert parameter '$parameterName' to $type", cause)
797
798
/**
799
* Thrown when a resource is not found
800
*/
801
class NotFoundException(message: String? = "Resource not found") : Exception(message)
802
803
/**
804
* Base exception for content transformation failures
805
*/
806
abstract class ContentTransformationException(message: String) : IOException(message)
807
808
/**
809
* Thrown when content cannot be transformed to the requested type
810
*/
811
class CannotTransformContentToTypeException(private val type: KType) : ContentTransformationException(
812
"Cannot transform content to $type"
813
)
814
815
/**
816
* Thrown when the request content type is not supported
817
*/
818
class UnsupportedMediaTypeException(private val contentType: ContentType?) : ContentTransformationException(
819
"Content type $contentType is not supported"
820
)
821
822
/**
823
* Thrown when request payload exceeds size limits
824
*/
825
class PayloadTooLargeException(private val sizeLimit: Long) : ContentTransformationException(
826
"Request payload size exceeds limit of $sizeLimit bytes"
827
)
828
```