馃摜 Response & JSON

KNETResponse zawiera wszystkie dane odpowiedzi HTTP z wygodnymi metodami do parsowania.

KNETResponse - struktura

data class KNETResponse(
    val statusCode: Int,           // 200, 404, 500...
    val statusMessage: String,     // "OK", "Not Found"...
    val headers: Map<String, String>,
    val bodyString: String,        // Raw body
    val bodyBytes: ByteArray?,     // Binary body
    val responseTimeMs: Long,      // Czas odpowiedzi
    val request: KNETRequest       // Oryginalny request
) {
    val isSuccessful: Boolean      // statusCode in 200..299
    val isClientError: Boolean     // statusCode in 400..499
    val isServerError: Boolean     // statusCode in 500..599
    val contentType: String?       // Content-Type header
    val contentLength: Long?       // Content-Length header
}

Sprawdzanie statusu

val response = client.get("https://api.example.com/users")

// Sprawd藕 sukces
if (response.isSuccessful) {
    processData(response.bodyString)
}

// Sprawd藕 konkretny status
when (response.statusCode) {
    200 -> handleSuccess(response)
    201 -> handleCreated(response)
    204 -> handleNoContent()
    400 -> handleBadRequest(response)
    401 -> handleUnauthorized()
    403 -> handleForbidden()
    404 -> handleNotFound()
    429 -> handleRateLimited(response)
    500, 502, 503 -> handleServerError(response)
    else -> handleUnknown(response)
}

// Lub uproszczone
when {
    response.isSuccessful -> processSuccess(response)
    response.isClientError -> handleClientError(response)
    response.isServerError -> handleServerError(response)
}

Parsowanie JSON

Do Map

val response = client.get("https://api.example.com/user/1")

// Ca艂y obiekt jako Map
val user = response.jsonObject()
println(user["name"])    // "Jan Kowalski"
println(user["email"])   // "jan@example.com"
println(user["age"])     // 30 (jako Any)

// Bezpieczny dost臋p
val name = user["name"] as? String ?: "Unknown"
val age = (user["age"] as? Number)?.toInt() ?: 0

Do List

val response = client.get("https://api.example.com/users")

// Lista obiekt贸w
val users = response.jsonArray()

users.forEach { userMap ->
    val user = userMap as Map<*, *>
    println("${user["id"]}: ${user["name"]}")
}

JSON Path - zagnie偶d偶one pola

// Response: {"user": {"profile": {"city": "Warszawa"}}}

val city = response.jsonPath("user.profile.city")
// "Warszawa"

val country = response.jsonPath("user.profile.country") ?: "Polska"
// Domy艣lna warto艣膰 je艣li brak

// Tablice
// Response: {"users": [{"name": "Jan"}, {"name": "Anna"}]}
val firstName = response.jsonPath("users.0.name")
// "Jan"

Deserializacja do obiekt贸w

Pojedynczy obiekt

data class User(
    val id: Int,
    val name: String,
    val email: String,
    val age: Int? = null
)

val response = client.get("https://api.example.com/users/1")
val user = response.json<User>()

println(user.name)   // "Jan Kowalski"
println(user.email)  // "jan@example.com"

Lista obiekt贸w

val response = client.get("https://api.example.com/users")
val users = response.jsonList<User>()

users.forEach { user ->
    println("${user.id}: ${user.name}")
}

// Lub z filtrowaniem
val activeUsers = response.jsonList<User>()
    .filter { it.age != null && it.age >= 18 }

Zagnie偶d偶one obiekty

data class Address(
    val street: String,
    val city: String,
    val zipCode: String
)

data class UserWithAddress(
    val id: Int,
    val name: String,
    val address: Address
)

val user = response.json<UserWithAddress>()
println(user.address.city)  // "Warszawa"

API Response wrapper

// Typowy response: {"success": true, "data": {...}, "message": "OK"}
data class ApiResponse<T>(
    val success: Boolean,
    val data: T?,
    val message: String?
)

data class UserData(val id: Int, val name: String)

val response = client.get("https://api.example.com/users/1")
val apiResponse = response.json<ApiResponse<UserData>>()

if (apiResponse.success && apiResponse.data != null) {
    println(apiResponse.data.name)
} else {
    println("Error: ${apiResponse.message}")
}

Inne formaty

XML

val xmlData = response.parseXml()
// Zwraca Map reprezentuj膮cy struktur臋 XML

// Dla: <user><name>Jan</name></user>
val name = (xmlData["user"] as? Map<*,*>)?.get("name")
// "Jan"

CSV

val csvData = response.parseCsv()
// Zwraca List<Map<String, String>>

// Dla: "name,email\nJan,jan@x.com\nAnna,anna@x.com"
csvData.forEach { row ->
    println("${row["name"]}: ${row["email"]}")
}
// Jan: jan@x.com
// Anna: anna@x.com

Form URL-Encoded

val formData = response.parseFormUrlEncoded()
// Zwraca Map<String, String>

// Dla: "name=Jan&email=jan%40example.com"
println(formData["name"])   // "Jan"
println(formData["email"])  // "jan@example.com"

Nag艂贸wki odpowiedzi

val response = client.get(url)

// Dost臋p do nag艂贸wk贸w
val contentType = response.headers["Content-Type"]
val cacheControl = response.headers["Cache-Control"]
val etag = response.headers["ETag"]

// W艂a艣ciwo艣ci pomocnicze
val size = response.contentLength  // Long?
val type = response.contentType    // String?

// Wszystkie nag艂贸wki
response.headers.forEach { (name, value) ->
    println("$name: $value")
}

Binary data

val response = client.get("https://example.com/image.png")

// Pobierz jako ByteArray
val imageBytes = response.bodyBytes

// Zapisz do pliku
if (imageBytes != null) {
    File("downloaded.png").writeBytes(imageBytes)
}

// Konwertuj do Bitmap (Android)
val bitmap = BitmapFactory.decodeByteArray(
    imageBytes, 0, imageBytes?.size ?: 0
)

Safe API - bez wyj膮tk贸w

// Zamiast try-catch u偶yj Safe API
client.getSafe("https://api.example.com/users")
    .onSuccess { response ->
        val users = response.jsonList<User>()
        updateUI(users)
    }
    .onFailure { error ->
        when (error) {
            is KNETError.NetworkError -> {
                showMessage("Brak po艂膮czenia")
            }
            is KNETError.HttpError -> {
                showMessage("B艂膮d serwera: ${error.statusCode}")
            }
            is KNETError.TimeoutError -> {
                showMessage("Przekroczono czas oczekiwania")
            }
            is KNETError.ParseError -> {
                showMessage("B艂膮d parsowania danych")
            }
            else -> {
                showMessage("Nieznany b艂膮d")
            }
        }
    }

// Lub jako Result
val result = client.getSafe("https://api.example.com/users")

if (result.isSuccess) {
    val response = result.getOrNull()!!
    processData(response)
} else {
    val error = result.exceptionOrNull()
    handleError(error)
}

Walidacja odpowiedzi

val response = client.get(url)

// Assertions
response.assertSuccessful()  // Throws je艣li nie 2xx
response.assertStatus(200)   // Throws je艣li nie 200
response.assertHasBody()     // Throws je艣li puste body
response.assertContentType("application/json")

// Chainable
val user = client.get("https://api.example.com/users/1")
    .assertSuccessful()
    .assertContentType("application/json")
    .json<User>()

馃摎 Zobacz te偶