HTTP-Anfrage in Kotlin

73

Ich bin völlig neu in Kotlin. Ich möchte eine Anmeldeüberprüfung mit der POST-Methode durchführen und einige Informationen mit der GET-Methode abrufen. Ich habe bereits URL, Server-Benutzername und Passwort meines vorherigen Projekts. Ich habe kein richtiges Beispielprojekt gefunden, das dieses Ding verwendet. Jeder schlägt mir bitte ein Arbeitsbeispiel vor, in dem ich die GET- und POST-Methode in HTTP-Anfragen verwenden kann

Rajkumar K.
quelle
3
Haben Sie versucht , Retrofit zu verwenden ? Sie finden Retrofit hier
EpicPandaForce

Antworten:

53

Für Android ist Volley ein guter Ort, um loszulegen. Für alle Plattformen möchten Sie möglicherweise auch ktor client oder http4k ausprobieren, die beide gute Bibliotheken sind.

Sie können jedoch auch Standard-Java-Bibliotheken verwenden, java.net.HttpURLConnection die Teil des Java SDK sind:

fun sendGet() {
    val url = URL("http://www.google.com/")

    with(url.openConnection() as HttpURLConnection) {
        requestMethod = "GET"  // optional default is GET

        println("\nSent 'GET' request to URL : $url; Response Code : $responseCode")

        inputStream.bufferedReader().use {
            it.lines().forEach { line ->
                println(line)
            }
        }
    }
}

Oder einfacher:

URL("https://google.com").readText()
s1m0nw1
quelle
3
Einige Apache-Komponenten sind in Java Android als veraltet markiert. Es ist besser, die Volley-Bibliothek für HTTP-Anforderungen zu verwenden
Thecave3
2
Okay, ich habe das Android-Tag nicht gesehen, die Frage erwähnte Kotlin hier nur
s1m0nw1
Ich weiß nicht, ob Kotlin außerhalb von Android verwendet werden kann, aber ich weiß wirklich, dass Apache unter Android nicht so funktioniert, wie es sein sollte
Thecave3
9
Ja sicher, dass es außerhalb von Android verwendet werden kann. Kotlin war ursprünglich nie für Android vorgesehen
;-)
Wie gebe ich den Benutzernamen und das Passwort des Servers an?
Rajkumar K
21

Senden Sie eine HTTP-POST / GET-Anforderung mit Parametern mithilfe von HttpURLConnection:

POST mit Parametern:

fun sendPostRequest(userName:String, password:String) {

    var reqParam = URLEncoder.encode("username", "UTF-8") + "=" + URLEncoder.encode(userName, "UTF-8")
    reqParam += "&" + URLEncoder.encode("password", "UTF-8") + "=" + URLEncoder.encode(password, "UTF-8")
    val mURL = URL("<Your API Link>")

    with(mURL.openConnection() as HttpURLConnection) {
        // optional default is GET
        requestMethod = "POST"

        val wr = OutputStreamWriter(getOutputStream());
        wr.write(reqParam);
        wr.flush();

        println("URL : $url")
        println("Response Code : $responseCode")

        BufferedReader(InputStreamReader(inputStream)).use {
            val response = StringBuffer()

            var inputLine = it.readLine()
            while (inputLine != null) {
                response.append(inputLine)
                inputLine = it.readLine()
            }
            println("Response : $response")
        }
    }
}

GET mit Parametern:

fun sendGetRequest(userName:String, password:String) {

        var reqParam = URLEncoder.encode("username", "UTF-8") + "=" + URLEncoder.encode(userName, "UTF-8")
        reqParam += "&" + URLEncoder.encode("password", "UTF-8") + "=" + URLEncoder.encode(password, "UTF-8")

        val mURL = URL("<Yout API Link>?"+reqParam)

        with(mURL.openConnection() as HttpURLConnection) {
            // optional default is GET
            requestMethod = "GET"

            println("URL : $url")
            println("Response Code : $responseCode")

            BufferedReader(InputStreamReader(inputStream)).use {
                val response = StringBuffer()

                var inputLine = it.readLine()
                while (inputLine != null) {
                    response.append(inputLine)
                    inputLine = it.readLine()
                }
                it.close()
                println("Response : $response")
            }
        }
    }
Deven
quelle
wo sind urlund inputStreamvars definiert? Dieser Code ist unvollständig.
Jungledev
2
BufferedReader implementiert AutoClosable, sodass Sie nicht explizit aufrufen müssen it.close.
Kwmt
8

Vielleicht das einfachste GET

Für alle, die mit NetworkOnMainThreadException für die anderen Lösungen nicht weiterkommen: Verwenden Sie AsyncTask oder, noch kürzer, (noch experimentell) Coroutines:

launch {

    val jsonStr = URL("url").readText()

}

Wenn Sie mit einfachem http testen müssen, vergessen Sie nicht, Ihrem Manifest Folgendes hinzuzufügen: android:usesCleartextTraffic="true"


Für die experimentellen Coroutinen müssen Sie build.gradle ab dem 10.10.2008 hinzufügen:

kotlin {
    experimental {
        coroutines 'enable'
    }
}
dependencies {
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:0.24.0"
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:0.24.0"
    ...
0llie
quelle
Hm, Android Studio sagt "Die Unterstützung für experimentelle Coroutinen wird in 1.4 eingestellt"
xjcl
Es sieht so aus, als wären Coroutinen aus dem Experiment herausgenommen worden! Auch die Startsyntax ist jetzt etwas anders.
24.
Entschuldigung, ich schon wieder. Meine IDE sagt mir "unangemessener Aufruf der Blockierungsmethode" für readText- ist es in Ordnung, readTextCoroutinen anstelle von Threads zu verwenden?
27.
6

Wenn Sie verwenden Kotlin, können Sie Ihren Code so kurz wie möglich halten. Die runMethode verwandelt den Empfänger in thisund gibt den Wert des Blocks zurück. this as HttpURLConnectionerstellt eine intelligente Besetzung. bufferedReader().readText()vermeidet eine Menge Boilerplate-Code.

return URL(url).run {
        openConnection().run {
            this as HttpURLConnection
            inputStream.bufferedReader().readText()
        }
}

Sie können dies auch in eine Erweiterungsfunktion einbinden.

fun URL.getText(): String {
    return openConnection().run {
                this as HttpURLConnection
                inputStream.bufferedReader().readText()
            }
}

Und nenne es so

return URL(url).getText()

Wenn Sie sehr faul sind, können Sie Stringstattdessen die Klasse erweitern.

fun String.getUrlText(): String {
    return URL(this).run {
            openConnection().run {
                this as HttpURLConnection
                inputStream.bufferedReader().readText()
            }
    }
}

Und nenne es so

return "http://somewhere.com".getUrlText()
Steven Spungin
quelle
Vergessen Sie nicht, das zu schließen InputStream, wieinputStream.bufferedReader().use {readText()}
Abhijit Sarkar
5

Schauen Sie sich die Kraftstoffbibliothek an , eine Beispiel- GET-Anfrage

"https://httpbin.org/get"
  .httpGet()
  .responseString { request, response, result ->
    when (result) {
      is Result.Failure -> {
        val ex = result.getException()
      }
      is Result.Success -> {
        val data = result.get()
      }
    }
  }

// You can also use Fuel.get("https://httpbin.org/get").responseString { ... }
// You can also use FuelManager.instance.get("...").responseString { ... }

Eine Beispiel- POST-Anfrage

Fuel.post("https://httpbin.org/post")
    .jsonBody("{ \"foo\" : \"bar\" }")
    .also { println(it) }
    .response { result -> }

Ihre Dokumentation findet sich hier

Gastrodon
quelle
4

Ich denke, die Verwendung von okhttp ist die einfachste Lösung. Hier sehen Sie ein Beispiel für die POST-Methode, das Senden eines JSON und mit auth.

val url = "https://example.com/endpoint"

val client = OkHttpClient()

val JSON = MediaType.get("application/json; charset=utf-8")
val body = RequestBody.create(JSON, "{\"data\":\"$data\"}")
val request = Request.Builder()
        .addHeader("Authorization", "Bearer $token")
        .url(url)
        .post(body)
        .build()

val  response = client . newCall (request).execute()

println(response.request())
println(response.body()!!.string())

Denken Sie daran, diese Abhängigkeit zu Ihrem Projekt https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp hinzuzufügen

UPDATE: 7. Juli 2019 Ich werde zwei Beispiele mit dem neuesten Kotlin (1.3.41), OkHttp (4.0.0) und Jackson (2.9.9) geben.

Methode abrufen

fun get() {
    val client = OkHttpClient()
    val url = URL("https://reqres.in/api/users?page=2")

    val request = Request.Builder()
            .url(url)
            .get()
            .build()

    val response = client.newCall(request).execute()

    val responseBody = response.body!!.string()

    //Response
    println("Response Body: " + responseBody)

    //we could use jackson if we got a JSON
    val mapperAll = ObjectMapper()
    val objData = mapperAll.readTree(responseBody)

    objData.get("data").forEachIndexed { index, jsonNode ->
        println("$index $jsonNode")
    }
}

POST-Methode

fun post() {
    val client = OkHttpClient()
    val url = URL("https://reqres.in/api/users")

    //just a string
    var jsonString = "{\"name\": \"Rolando\", \"job\": \"Fakeador\"}"

    //or using jackson
    val mapperAll = ObjectMapper()
    val jacksonObj = mapperAll.createObjectNode()
    jacksonObj.put("name", "Rolando")
    jacksonObj.put("job", "Fakeador")
    val jacksonString = jacksonObj.toString()

    val mediaType = "application/json; charset=utf-8".toMediaType()
    val body = jacksonString.toRequestBody(mediaType)

    val request = Request.Builder()
            .url(url)
            .post(body)
            .build()

    val response = client.newCall(request).execute()

    val responseBody = response.body!!.string()

    //Response
    println("Response Body: " + responseBody)

    //we could use jackson if we got a JSON
    val objData = mapperAll.readTree(responseBody)

    println("My name is " + objData.get("name").textValue() + ", and I'm a " + objData.get("job").textValue() + ".")
}
fvildoso
quelle
1
Vielen Dank. Das hat bei mir funktioniert. Aber RequestBody.created () ist jetzt veraltet. Was ist die Lösung ?
Abhijith K
Was importierst du? Ich kann nicht herausfinden, welches "Get" es sein soll, das zu dem passt, was ich für okhttp MediaType halte ... Und was sind "$ data" und "$ token"? Sagt auch "Kann nicht auf 'body' zugreifen: es ist privat in 'Response'" in "val responseBody = response.body !!. String ()"
Raksha
@Raksha importieren com.fasterxml.jackson.databind.ObjectMapper importieren okhttp3.MediaType.Companion.toMediaType importieren okhttp3.OkHttpClient importieren okhttp3.Request importieren java.net.URL importieren okhttp3.
fvildoso
4

Sie können die Kohttp- Bibliothek verwenden. Es ist ein Kotlin DSL HTTP-Client. Es unterstützt die Funktionen von square.okhttp und bietet ihnen ein klares DSL. Asynchrone KoHttp-Aufrufe werden von Coroutinen unterstützt.

httpGet Erweiterungsfunktion

val response: Response = "https://google.com/search?q=iphone".httpGet()

Sie können auch einen asynchronen Aufruf mit Coroutinen verwenden

val response: Deferred<Response> = "https://google.com/search?q=iphone".asyncHttpGet()

oder DSL-Funktion für komplexere Anforderungen

val response: Response = httpGet {
    host = "google.com"
    path = "/search"
    param {
       "q" to "iphone"
       "safe" to "off"
   }
}

Weitere Details finden Sie in den Dokumenten

Um es mit Gradle zu bekommen, benutze

implementation 'io.github.rybalkinsd:kohttp:0.12.0'
Sergei Rybalkin
quelle
Es hängt von Ihren Bedürfnissen ab. Die GET-Methode ist so deklariert, fun httpGet(client: Call.Factory = defaultHttpClient, init: HttpGetContext.() -> Unit): Responsedass sie Call Factory mit einer Konfiguration verwenden kann, die für Ihre Projektanforderungen und Leistungsprobleme spezifisch ist.
Sergei Rybalkin
4

Verwenden Sie nur die Standardbibliothek mit minimalem Code!

thread {
    val jsonStr = try { URL(url).readText() } catch (ex: Exception) { return@thread }
    runOnUiThread { displayOrWhatever(jsonStr) }
}

Dadurch wird eine GET-Anforderung für einen neuen Thread gestartet , sodass der UI-Thread auf Benutzereingaben reagieren kann. Wir können jedoch nur UI-Elemente aus dem Haupt- / UI-Thread ändern, sodass wir tatsächlich einen runOnUiThreadBlock benötigen , um das Ergebnis unserem Benutzer anzuzeigen. Dadurch wird unser Anzeigecode in die Warteschlange gestellt, damit er bald auf dem UI-Thread ausgeführt werden kann.

Der Versuch / Fang ist da, damit Ihre App nicht abstürzt, wenn Sie eine Anfrage mit ausgeschaltetem Internet Ihres Telefons stellen. Fügen Sie nach Belieben Ihre eigene Fehlerbehandlung hinzu (z. B. Anzeigen eines Toasts).

.readText()ist nicht Teil der java.net.URLKlasse, sondern eine Kotlin- Erweiterungsmethode . Kotlin "klebt" diese Methode auf URL. Dies reicht für einfache GET-Anforderungen aus, aber für mehr Kontrolle und POST-Anforderungen benötigen Sie so etwas wie die FuelBibliothek.

xjcl
quelle
Warum verpacken Sie den Funktionskörper nicht einfach wie unten beschrieben, anstatt nur (nützliche) Warnungen zu machen? <pre> var worker = Objekt: AsyncTask <Void, Void, Void?> () {Spaß überschreiben doInBackground (Parameter: Array <Void>): Void? {Runner () return null} Override-Spaß onPostExecute (Ergebnis: Void?) {super.onPostExecute (Ergebnis) / * Mach etwas mit Ergebnis * /}} worker.execute () </ pre>
Marco Ottina
1
Hey Marco, ich verwende derzeit auch AsyncTask , aber es wird in Android 11 veraltet sein . Und ich habe noch nicht mit anderen Ansätzen experimentiert, daher fühle ich mich noch nicht wohl, sie zu erklären. Ich werde meine Antwort später bearbeiten.
xjcl
1
@MarcoOttina Ich habe jetzt ein vollständiges Codebeispiel hinzugefügt
xjcl
3
import java.io.IOException
import java.net.URL

fun main(vararg args: String) {
    val response = try {
        URL("http://seznam.cz")
                .openStream()
                .bufferedReader()
                .use { it.readText() }
    } catch (e: IOException) {
        "Error with ${e.message}."
    }
    println(response)
}
Pavel Hanpari
quelle
1
Dies funktioniert gut außerhalb von Android, aber das war nicht das, wonach die Frage gefragt hatte
OneCricketeer
3

Ohne zusätzliche Abhängigkeiten hinzuzufügen, funktioniert dies. Dafür brauchst du keinen Volley. Dies funktioniert mit der aktuellen Version von Kotlin ab Dezember 2018: Kotlin 1.3.10

Wenn Sie Android Studio verwenden, müssen Sie diese Deklaration in Ihre AndroidManifest.xml einfügen:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

Sie sollten Importe hier manuell deklarieren. Das Auto-Import-Tool hat mir viele Konflikte verursacht:

import android.os.AsyncTask
import java.io.BufferedReader
import java.io.InputStreamReader
import java.io.OutputStream
import java.io.OutputStreamWriter
import java.net.URL
import java.net.URLEncoder
import javax.net.ssl.HttpsURLConnection

Sie können keine Netzwerkanforderungen für einen Hintergrundthread ausführen. Sie müssen Unterklasse AsyncTask.

So rufen Sie die Methode auf:

NetworkTask().execute(requestURL, queryString)

Erklärung:

private class NetworkTask : AsyncTask<String, Int, Long>() {
    override fun doInBackground(vararg parts: String): Long? {
        val requestURL = parts.first()
        val queryString = parts.last()

        // Set up request
        val connection: HttpsURLConnection = URL(requestURL).openConnection() as HttpsURLConnection
        // Default is GET so you must override this for post
        connection.requestMethod = "POST"
        // To send a post body, output must be true
        connection.doOutput = true
        // Create the stream
        val outputStream: OutputStream = connection.outputStream
        // Create a writer container to pass the output over the stream
        val outputWriter = OutputStreamWriter(outputStream)
        // Add the string to the writer container
        outputWriter.write(queryString)
        // Send the data
        outputWriter.flush()

        // Create an input stream to read the response
        val inputStream = BufferedReader(InputStreamReader(connection.inputStream)).use {
            // Container for input stream data
            val response = StringBuffer()
            var inputLine = it.readLine()
            // Add each line to the response container
            while (inputLine != null) {
                response.append(inputLine)
                inputLine = it.readLine()
            }
            it.close()
            // TODO: Add main thread callback to parse response
            println(">>>> Response: $response")
        }
        connection.disconnect()

        return 0
    }

    protected fun onProgressUpdate(vararg progress: Int) {
    }

    override fun onPostExecute(result: Long?) {
    }
}
Jungledev
quelle
1

GET und POST mit OkHttp

private const val CONNECT_TIMEOUT = 15L
private const val READ_TIMEOUT = 15L
private const val WRITE_TIMEOUT = 15L

private fun performPostOperation(urlString: String, jsonString: String, token: String): String? {
    return try {
        val client = OkHttpClient.Builder()
            .connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS)
            .writeTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS)
            .readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
            .build()

        val body = jsonString.toRequestBody("application/json; charset=utf-8".toMediaTypeOrNull())

        val request = Request.Builder()
            .url(URL(urlString))
            .header("Authorization", token)
            .post(body)
            .build()

        val response = client.newCall(request).execute()
        response.body?.string()
    }
    catch (e: IOException) {
        e.printStackTrace()
        null
    }
}

private fun performGetOperation(urlString: String, token: String): String? {
    return try {
        val client = OkHttpClient.Builder()
            .connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS)
            .writeTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS)
            .readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
            .build()

        val request = Request.Builder()
            .url(URL(urlString))
            .header("Authorization", token)
            .get()
            .build()

        val response = client.newCall(request).execute()
        response.body?.string()
    }
    catch (e: IOException) {
        e.printStackTrace()
        null
    }
}

Objektserialisierung und Deserialisierung

@Throws(JsonProcessingException::class)
fun objectToJson(obj: Any): String {
    return ObjectMapper().writeValueAsString(obj)
}

@Throws(IOException::class)
fun jsonToAgentObject(json: String?): MyObject? {
    return if (json == null) { null } else {
        ObjectMapper().readValue<MyObject>(json, MyObject::class.java)
    }
}

Abhängigkeiten

Fügen Sie die folgenden Zeilen in Ihre Gradle ( App ) -Datei ein. Jackson ist optional. Sie können es für die Serialisierung und Deserialisierung von Objekten verwenden.

implementation 'com.squareup.okhttp3:okhttp:4.3.1'
implementation 'com.fasterxml.jackson.core:jackson-core:2.9.8'
implementation 'com.fasterxml.jackson.core:jackson-annotations:2.9.8'
implementation 'com.fasterxml.jackson.core:jackson-databind:2.9.8'
Mateus Melo
quelle