0
# Response Handling
1
2
The response handling API provides comprehensive functionality for accessing response data, headers, status information, and converting response bodies to various types with streaming support.
3
4
## Core Types
5
6
```kotlin { .api }
7
abstract class HttpResponse : HttpMessage, CoroutineScope {
8
public abstract val call: HttpClientCall
9
public abstract val status: HttpStatusCode
10
public abstract val version: HttpProtocolVersion
11
public abstract val requestTime: GMTDate
12
public abstract val responseTime: GMTDate
13
14
override fun toString(): String
15
}
16
17
class HttpStatement(
18
private val builder: HttpRequestBuilder,
19
internal val client: HttpClient
20
) {
21
suspend fun execute(): HttpResponse
22
suspend fun <T> execute(block: suspend (response: HttpResponse) -> T): T
23
suspend inline fun <reified T> body(): T
24
}
25
26
// Extension properties and functions for HttpResponse
27
val HttpResponse.request: HttpRequest
28
29
// Response body reading extensions
30
suspend fun HttpResponse.bodyAsText(fallbackCharset: Charset = Charsets.UTF_8): String
31
suspend fun HttpResponse.bodyAsChannel(): ByteReadChannel
32
suspend fun HttpResponse.bodyAsBytes(): ByteArray
33
```
34
35
## Response Properties
36
37
### Status Information
38
```kotlin
39
val response = client.get("https://api.example.com/users")
40
41
println("Status: ${response.status}") // Status: 200 OK
42
println("Status code: ${response.status.value}") // Status code: 200
43
println("Status description: ${response.status.description}") // Status description: OK
44
45
// Status checks
46
if (response.status.isSuccess()) {
47
println("Request succeeded")
48
}
49
50
when (response.status.value) {
51
200 -> println("OK")
52
404 -> println("Not Found")
53
500 -> println("Server Error")
54
}
55
```
56
57
### Headers Access
58
```kotlin
59
val response = client.get("https://api.example.com/users")
60
61
// Individual headers
62
val contentType = response.headers["Content-Type"]
63
val contentLength = response.headers["Content-Length"]?.toLongOrNull()
64
val server = response.headers["Server"]
65
66
// All headers
67
response.headers.forEach { name, values ->
68
println("$name: ${values.joinToString(", ")}")
69
}
70
71
// Typed header access
72
val etag = response.headers[HttpHeaders.ETag]
73
val lastModified = response.headers[HttpHeaders.LastModified]
74
```
75
76
### Timing Information
77
```kotlin
78
val response = client.get("https://api.example.com/users")
79
80
println("Request time: ${response.requestTime}")
81
println("Response time: ${response.responseTime}")
82
83
val duration = response.responseTime.timestamp - response.requestTime.timestamp
84
println("Request took: ${duration}ms")
85
```
86
87
### Protocol Information
88
```kotlin
89
val response = client.get("https://api.example.com/users")
90
91
println("HTTP version: ${response.version}") // HTTP/1.1, HTTP/2.0, etc.
92
```
93
94
## Reading Response Body
95
96
### Text Content
97
```kotlin { .api }
98
suspend fun HttpResponse.bodyAsText(
99
charset: Charset = Charsets.UTF_8
100
): String
101
102
suspend fun HttpResponse.bodyAsText(
103
fallbackCharset: Charset = Charsets.UTF_8
104
): String
105
```
106
107
```kotlin
108
val response = client.get("https://api.example.com/users")
109
val text = response.bodyAsText()
110
println(text)
111
112
// With specific charset
113
val textUtf8 = response.bodyAsText(Charsets.UTF_8)
114
```
115
116
### Binary Content
117
```kotlin { .api }
118
suspend fun HttpResponse.bodyAsBytes(): ByteArray
119
```
120
121
```kotlin
122
val response = client.get("https://api.example.com/image.jpg")
123
val bytes = response.bodyAsBytes()
124
File("downloaded-image.jpg").writeBytes(bytes)
125
```
126
127
### Channel Content
128
```kotlin { .api }
129
fun HttpResponse.bodyAsChannel(): ByteReadChannel
130
```
131
132
```kotlin
133
val response = client.get("https://api.example.com/large-file")
134
val channel = response.bodyAsChannel()
135
136
// Stream processing
137
while (!channel.isClosedForRead) {
138
val packet = channel.readRemaining(8192)
139
// Process packet
140
}
141
```
142
143
### Deserialized Content
144
With appropriate serialization plugins (like kotlinx.serialization):
145
146
```kotlin
147
@Serializable
148
data class User(val id: Int, val name: String, val email: String)
149
150
val response = client.get("https://api.example.com/users/123")
151
val user = response.body<User>()
152
println("User: ${user.name}")
153
```
154
155
## Response Streaming
156
157
### Streaming Large Responses
158
```kotlin
159
val response = client.get("https://api.example.com/large-dataset")
160
val channel = response.bodyAsChannel()
161
162
val outputFile = File("dataset.json")
163
outputFile.outputStream().use { output ->
164
while (!channel.isClosedForRead) {
165
val packet = channel.readRemaining(8192)
166
output.write(packet.readBytes())
167
}
168
}
169
```
170
171
### Progress Monitoring
172
With the BodyProgress plugin:
173
174
```kotlin
175
client.get("https://api.example.com/large-file") {
176
onDownload { bytesSentTotal, contentLength ->
177
val progress = (bytesSentTotal * 100 / contentLength).toInt()
178
println("Download progress: $progress%")
179
}
180
}.bodyAsBytes()
181
```
182
183
## Statement Execution
184
185
### Basic Statement Execution
186
```kotlin
187
val statement = client.prepareGet("https://api.example.com/users")
188
val response = statement.execute()
189
val text = response.bodyAsText()
190
```
191
192
### Statement with Response Processing
193
```kotlin
194
val statement = client.prepareGet("https://api.example.com/users")
195
196
val users = statement.execute { response ->
197
if (response.status.isSuccess()) {
198
response.bodyAsText()
199
} else {
200
throw RuntimeException("Request failed: ${response.status}")
201
}
202
}
203
```
204
205
### Reusable Statements
206
```kotlin
207
val userStatement = client.prepareGet {
208
url("https://api.example.com/users")
209
header("Authorization", "Bearer $token")
210
}
211
212
// Use multiple times
213
val page1 = userStatement.execute {
214
parameter("page", 1)
215
bodyAsText()
216
}
217
218
val page2 = userStatement.execute {
219
parameter("page", 2)
220
bodyAsText()
221
}
222
```
223
224
## Error Response Handling
225
226
### Status Code Checking
227
```kotlin
228
val response = client.get("https://api.example.com/users/999")
229
230
when {
231
response.status.isSuccess() -> {
232
val user = response.bodyAsText()
233
println("User: $user")
234
}
235
response.status.value == 404 -> {
236
println("User not found")
237
}
238
response.status.value in 400..499 -> {
239
val errorBody = response.bodyAsText()
240
println("Client error: $errorBody")
241
}
242
response.status.value in 500..599 -> {
243
println("Server error: ${response.status}")
244
}
245
}
246
```
247
248
### Exception Handling with Call Validator
249
```kotlin
250
client.get("https://api.example.com/users") {
251
// With HttpCallValidator plugin, non-success responses throw exceptions
252
}
253
```
254
255
## Response Validation
256
257
### Content Type Validation
258
```kotlin
259
val response = client.get("https://api.example.com/users")
260
val contentType = response.headers[HttpHeaders.ContentType]
261
262
if (contentType?.startsWith("application/json") == true) {
263
val json = response.bodyAsText()
264
// Process JSON
265
} else {
266
println("Unexpected content type: $contentType")
267
}
268
```
269
270
### Content Length Validation
271
```kotlin
272
val response = client.get("https://api.example.com/file")
273
val contentLength = response.headers[HttpHeaders.ContentLength]?.toLongOrNull()
274
val actualBytes = response.bodyAsBytes()
275
276
if (contentLength != null && actualBytes.size.toLong() != contentLength) {
277
println("Content length mismatch!")
278
}
279
```
280
281
## Response Caching
282
283
### Cache Headers
284
```kotlin
285
val response = client.get("https://api.example.com/users")
286
287
val cacheControl = response.headers[HttpHeaders.CacheControl]
288
val etag = response.headers[HttpHeaders.ETag]
289
val expires = response.headers[HttpHeaders.Expires]
290
val lastModified = response.headers[HttpHeaders.LastModified]
291
292
println("Cache Control: $cacheControl")
293
println("ETag: $etag")
294
```
295
296
### Conditional Requests
297
```kotlin
298
// Using If-None-Match with ETag
299
val etag = "W/\"abc123\""
300
val response = client.get("https://api.example.com/users") {
301
header(HttpHeaders.IfNoneMatch, etag)
302
}
303
304
if (response.status.value == 304) {
305
println("Content not modified")
306
} else {
307
val newContent = response.bodyAsText()
308
val newEtag = response.headers[HttpHeaders.ETag]
309
}
310
```
311
312
## Response Context
313
314
### Coroutine Context
315
```kotlin
316
val response = client.get("https://api.example.com/users")
317
318
// Access coroutine context
319
val job = response.coroutineContext[Job]
320
val dispatcher = response.coroutineContext[CoroutineDispatcher]
321
322
// Use response as coroutine scope
323
response.launch {
324
// Background processing of response
325
}
326
```
327
328
### Call Information
329
```kotlin
330
val response = client.get("https://api.example.com/users")
331
332
println("Request URL: ${response.call.request.url}")
333
println("Request method: ${response.call.request.method}")
334
println("Response status: ${response.call.response.status}")
335
```