Kotlin和khttp的HTTP请求

HTTP Requests with Kotlin and khttp

1.简介

目前,基于HTTP协议和基于API的API在编程中至关重要。

在JVM上,我们有几个可用的选项,从较低级的库到非常高级的库,从已建立的项目到新的孩子。 但是,它们中的大多数主要针对Java程序。

在本文中,我们将研究khttp,这是一个惯用的Kotlin库,用于使用基于HTTP的资源和API。

2.依存关系

为了在我们的项目中使用该库,首先我们必须将其添加到我们的依赖项中:

1
2
3
4
5
<dependency>
    <groupId>khttp</groupId>
    <artifactId>khttp</artifactId>
    <version>0.1.0</version>
</dependency>

由于尚未在Maven Central上启用,因此我们还必须启用JCenter存储库:

1
2
3
4
<repository>
    <id>central</id>
    <url>http://jcenter.bintray.com</url>
</repository>

在撰写本文时,版本0.1.0是当前版本。 当然,我们可以检查JCenter以获得较新的版本。

3.基本用法

HTTP协议的基础很简单,即使细节很复杂。 因此,khttp也具有简单的界面。

对于每个HTTP方法,我们都可以在khttp包中找到一个包级函数,例如get,post等。

所有函数都使用相同的参数集并返回Response对象; 我们将在以下各节中详细介绍这些内容。

在本文的过程中,我们将使用完全限定的格式,例如khttp.put。 当然,在我们的项目中,我们可以导入并可能重命名这些方法:

1
import khttp.delete as httpDelete

注意:为了清楚起见,我们在代码示例中添加了类型声明,因为如果没有IDE,它们将很难遵循。

4.一个简单的请求

每个HTTP请求至少具有两个必需的组件:方法和URL。 在khttp中,方法由我们调用的函数确定,如上一节所述。

URL是该方法唯一需要的参数。 因此,我们可以轻松执行一个简单的请求:

1
khttp.get("http://httpbin.org/get")

在以下各节中,我们将考虑所有成功完成的请求。

4.1。 添加参数

除基本URL之外,我们通常还必须提供查询参数,尤其是对于GET请求。

khttp的方法接受params参数,该参数是要包含在查询String中的键/值对的Map:

1
2
3
khttp.get(
  url ="http://httpbin.org/get",
  params = mapOf("key1" to"value1","keyn" to"valuen"))

请注意,我们已经使用mapOf函数来动态构建Map; 最终的请求URL将是:

1
http://httpbin.org/get?key1=value1&keyn=valuen

5.请求主体

我们通常需要执行的另一个常见操作是发送数据,通常作为POST或PUT请求的有效负载。

为此,该库提供了几个选项,我们将在以下各节中进行研究。

5.1。 发送JSON有效负载

我们可以使用json参数发送JSON对象或数组。 它可以是几种不同的类型:

  • org.json库提供的JSONObject或JSONArray

  • 一个Map,转换为JSON对象

  • Collection,Iterable或数组,它转换为JSON数组

  • 我们可以轻松地将之前的GET示例转换为POST示例,该示例将发送一个简单的JSON对象:

    1
    2
    3
    khttp.post(
      url ="http://httpbin.org/post",
      json = mapOf("key1" to"value1","keyn" to"valuen"))

    请注意,从集合到JSON对象的转换很浅。 例如,地图列表不会转换为JSON对象的JSON数组,而是转换为字符串数组。

    为了进行深度转换,我们需要一个更复杂的JSON映射库,例如Jackson。 库的转换工具仅适用于简单情况。

    5.2。 发送表单数据(URL编码)

    要发送表单数据(URL编码,如HTML表单),我们将data参数与Map结合使用:

    1
    2
    3
    khttp.post(
      url ="http://httpbin.org/post",
      data = mapOf("key1" to"value1","keyn" to"valuen"))

    5.3。 上传文件(多部分表单)

    我们可以发送一个或多个编码为多部分表单数据请求的文件。

    在这种情况下,我们使用files参数:

    1
    2
    3
    4
    5
    khttp.post(
      url ="http://httpbin.org/post",
      files = listOf(
        FileLike("file1","content1"),
        FileLike("file2", File("kitty.jpg"))))

    我们可以看到http使用File Like抽象,这是一个具有名称和内容的对象。 内容可以是字符串,字节数组,文件或路径。

    5.4。 发送原始内容

    如果以上选项都不适合,我们可以使用InputStream发送原始数据作为HTTP请求的主体:

    1
    khttp.post(url ="http://httpbin.org/post", data = someInputStream)

    在这种情况下,我们很可能也需要手动设置一些标题,我们将在后面的部分中介绍这些标题。

    6.处理回应

    到目前为止,我们已经看到了将数据发送到服务器的各种方法。 但是许多HTTP操作还是有用的,因为它们也会返回数据。

    khttp基于阻塞的I / O,因此与HTTP方法相对应的所有功能都返回一个Response对象,其中包含从服务器接收到的响应。

    根据内容的类型,此对象具有可访问的各种属性。

    6.1。 JSON回应

    如果我们知道响应是JSON对象或数组,则可以使用jsonObject和jsonArray属性:

    1
    2
    3
    val response : Response = khttp.get("http://httpbin.org/get")
    val obj : JSONObject = response.jsonObject
    print(obj["someProperty"])

    6.2。 文字或二进制回复

    如果我们想将响应作为字符串读取,则可以使用text属性:

    1
    val message : String = response.text

    或者,如果我们想将其读取为二进制数据(例如文件下载),则可以使用content属性:

    1
    val imageData : ByteArray = response.content

    最后,我们还可以访问基础InputStream:

    1
    val inputStream : InputStream = response.raw

    7.高级用法

    让我们也看一下一些更有用的高级用法模式,这些模式通常是有用的,并且在上一节中我们还没有讨论过。

    7.1。 处理标题和Cookie

    所有khttp函数都采用headers参数,该参数是标头名称和值的映射。

    1
    2
    3
    val response = khttp.get(
      url ="http://httpbin.org/get",
      headers = mapOf("header1" to"1","header2" to"2"))

    对于Cookie同样:

    1
    2
    3
    val response = khttp.get(
      url ="http://httpbin.org/get",
      cookies = mapOf("cookie1" to"1","cookie2" to"2"))

    我们还可以在响应中访问服务器发送的标头和cookie:

    1
    2
    val contentType : String = response.headers["Content-Type"]
    val sessionID : String = response.cookies["JSESSIONID"]

    7.2。 处理错误

    HTTP中可能会产生两种类型的错误:错误响应,例如404 –未找到,是协议的一部分; 和低级错误,例如"连接被拒绝"。

    第一种不会导致khttp抛出异常。 相反,我们应该检查Response statusCode属性:

    1
    2
    3
    4
    5
    6
    val response = khttp.get(url ="http://httpbin.org/nothing/to/see/here")
    if(response.statusCode == 200) {
        process(response)
    } else {
        handleError(response)
    }

    相反,较低级别的错误导致底层Java I / O子系统(如ConnectException)引发异常。

    7.3。 流式响应

    有时,服务器可以响应大量内容,并且/或者花费很长时间做出响应。 在这些情况下,我们可能希望分块处理响应,而不是等待响应完成并占用内存。

    如果我们想指示库给我们一个流响应,那么我们必须传递true作为stream参数:

    1
    val response = khttp.get(url ="http://httpbin.org", stream = true)

    然后,我们可以分块处理它:

    1
    response.contentIterator(chunkSize = 1024).forEach { arr : ByteArray -> handleChunk(arr) }

    7.4。 非标准方法

    在极少数情况下,我们需要使用khttp本身没有提供的HTTP方法(或动词),例如,对于HTTP协议的某些扩展,例如WebDAV,我们仍然可以解决。

    实际上,khttp包中与HTTP方法相对应的所有函数都是使用通用请求函数实现的,我们也可以使用该函数:

    1
    2
    3
    4
    khttp.request(
      method ="COPY",
      url ="http://httpbin.org/get",
      headers = mapOf("Destination" to"/copy-of-get"))

    7.5。 其他特性

    我们还没有涉及khttp的所有功能。 例如,我们没有讨论超时,重定向和历史记录或异步操作。

    官方文档是有关库及其所有功能的最终信息来源。

    8.结论

    在本教程中,我们已经看到了如何使用惯用的库khttp在Kotlin中发出HTTP请求。

    所有这些示例的实现都可以在GitHub项目中找到–这是一个Maven项目,因此应易于导入和运行。