Kotlin中的HTTP请求


75

我是Kotlin的新手。我想使用POST方法进行登录验证,并使用GET方法获取一些信息。我已经有上一个项目的URL,服务器用户名和密码。我没有找到任何适当的示例项目使用此东西。任何人都可以向我建议任何可以在HTTP请求中使用GET和POST方法的工作示例


4
您是否尝试过使用Retrofit?您可以在此处
EpicPandaForce

Answers:


53

对于Android,Volley是入门的好地方。对于所有平台,您可能还希望检出ktor客户端或http4k都是不错的库。

但是,您也可以使用标准Java库,例如java.net.HttpURLConnection Java SDK的一部分:

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)
            }
        }
    }
}

或更简单:

URL("https://google.com").readText()

3
某些Apache组件在Java Android中被标记为已弃用,最好将Volley库用于HTTP请求
Thecave3

2
好的,我没有看到android标签,这里只提到了Kotlin的问题
s1m0nw1

我不知道kotlin是否可以在Android之外使用,但我真正知道Android上的Apache不能正常工作
Thecave3

9
是的,请确保它可以在Android之外使用。Kotlin最初从未针对Android定位;-)
s1m0nw1

1
不要忘了添加<uses-permission android:name="android.permission.INTERNET" />到AndroidManifest.xml
Josh Correia,

21

使用发送带有参数的HTTP POST / GET请求HttpURLConnection

带参数的POST:

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")
        }
    }
}

使用参数获取:

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")
            }
        }
    }

urlinputStreamvar在哪里定义?该代码不完整。
jungledev

2
BufferedReader实现AutoClosable,因此您无需显式调用it.close
kwmt19年

9

也许是最简单的GET

对于其他对NetworkOnMainThreadException棘手的人来说,请使用AsyncTask或更短的(但仍处于实验阶段)协程:

launch {

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

}

如果您需要使用简单的http测试,请不要忘记添加到清单中: android:usesCleartextTraffic="true"


从2018年10月10日起,您必须为build.gradle添加实验协同程序:

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"
    ...

嗯,Android Studio说“实验协同程序支持将在1.4中删除”
xjcl

看起来协程已经脱离实验性了!另外,启动语法现在也有所不同。
xjcl

抱歉,我又来了。我的IDE告诉我“不适当的阻塞方法调用”的原因readText-readText与协程而不是线程一起使用可以吗?
xjcl

6

如果使用Kotlin,则最好使代码尽可能简洁。该run方法将接收器变成this并返回该块的值。 this as HttpURLConnection创建一个聪明的演员表。 bufferedReader().readText()避免一堆样板代码。

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

您也可以将其包装到扩展功能中。

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

并这样称呼它

return URL(url).getText()

最后,如果您超级懒惰,则可以扩展String该类。

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

并这样称呼它

return "http://somewhere.com".getUrlText()

别忘了关闭InputStream,例如inputStream.bufferedReader().use {readText()}
Abhijit Sarkar

6

看一下Fuel库,一个示例GET请求

"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 { ... }

样本POST请求

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

他们的文档,可以发现这里


5

我认为使用okhttp是最简单的解决方案。在这里,您可以看到POST方法,发送json和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())

请记住将此依赖项添加到您的项目中https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp

更新:2019年7月7日, 我将使用最新的Kotlin(1.3.41),OkHttp(4.0.0)和Jackson(2.9.9)给出两个示例。

获取方法

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")
    }
}

开机自检方法

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() + ".")
}

1
谢谢。那对我有用。但是现在不推荐使用RequestBody.created()。解决办法是什么 ?
Abhijith K

你要进口什么?我不知道应该假定哪个“ get”与我假设的是okhttp MediaType ...以及“ $ data”和“ $ token”是什么?在“ val responseBody = response.body !!。string()”中还说“无法访问'body':它在'Response'中是私有的”
Raksha

@Raksha导入com.fasterxml.jackson.databind.ObjectMapper导入okhttp3.MediaType.Companion.toMediaType导入okhttp3.OkHttpClient导入okhttp3.Request导入java.net.URL导入okhttp3.RequestBody.Companion.toRequestBody您应尝试使用更新的示例。
fvildoso

4

您可以使用kohttp库。这是Kotlin DSL HTTP客户端。它支持square.okhttp的功能,并为其提供清晰的DSL。KoHttp异步调用由协程驱动。

httpGet 扩展功能

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

您还可以对协程使用异步调用

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

或DSL功能处理更复杂的请求

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

您可以在文档中找到更多详细信息

要使用gradle来获得它

implementation 'io.github.rybalkinsd:kohttp:0.12.0'

这取决于您的需求。GET方法声明为,fun httpGet(client: Call.Factory = defaultHttpClient, init: HttpGetContext.() -> Unit): Response因此可以将call factory与特定于您的项目需求和性能问题的配置一起使用。
Sergei Rybalkin

4

仅使用最少的代码使用标准库

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

这将在新线程上启动GET请求,而UI线程将响应用户输入。但是,我们只能从main / UI线程修改UI元素,因此实际上我们需要一个runOnUiThread块来向用户显示结果。这使我们的显示代码排队不久就可以在UI线程上运行。

try / catch语句是有,所以如果你与你的手机的互联网关的请求您的应用程序不会崩溃。您可以根据需要添加自己的错误处理(例如显示Toast)。

.readText()不是java.net.URL类的一部分,而是Kotlin扩展方法,Kotlin将此方法“粘合”到上URL。这对于普通的GET请求就足够了,但是对于更多的控制和POST请求,您需要类似Fuel库的内容。


为什么不按如下所述包装函数主体,而不是仅发出(有用的)警告?<pre> var worker = object:AsyncTask <Void,Void,Void?>(){重写fun doInBackground(params:Array <Void>):Void?{Runner()返回null}重写fun onPostExecute(result:Void?){super.onPostExecute(result)/ *对结果做些事* /}} worker.execute()</ pre>
Marco Ottina

1
嗨Marco,我目前也正在使用AsyncTask,但在Android 11中将不推荐使用。而且我还没有尝试过其他方法,因此我还不愿意解释它们,稍后我将编辑答案。
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)
}

1
Android之外,此方法运行良好,但这不是问题所要的
OneCricketeer

3

在不添加其他依赖项的情况下,此方法可行。 您不需要排球。可以使用截至2018年12月的当前Kotlin版本:Kotlin 1.3.10

如果使用Android Studio,则需要在AndroidManifest.xml中添加以下声明:

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

您应该在此处手动声明导入。自动导入工具引起了我很多冲突。

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

您不能在后台线程上执行网络请求。您必须继承AsyncTask

调用方法:

NetworkTask().execute(requestURL, queryString)

宣言:

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?) {
    }
}

1

使用OkHttp进行GET和POST

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
    }
}

对象序列化和反序列化

@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)
    }
}

依存关系

将以下行放入gradle(app)文件中。杰克逊是可选的。您可以将其用于对象序列化和反序列化。

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'
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.