KotlinによるFuel HTTPライブラリ

Kotlinを使用したHTTPライブラリの強化

1. 概要

In this tutorial, we’ll have a look at the Fuel HTTP Libraryは、著者の言葉では、Kotlin / Android用の最も簡単なHTTPネットワークライブラリです。 さらに、ライブラリはJavaでも使用できます。

ライブラリの主な機能は次のとおりです。

  • 非同期要求とブロッキング要求の両方の基本的なHTTP動詞(GET、POST、DELETEなど)のサポート

  • ファイルをダウンロードおよびアップロードする機能(multipart/form-data

  • グローバル構成を管理する可能性

  • 組み込みのオブジェクトシリアル化モジュール(Jackson、Gson、Mhosi、Forge)

  • KotlinのコルーチンモジュールとRxJava2.xのサポート

  • ルーターの設計パターンを簡単に設定

2. 依存関係

The library is composed of different modules so we can easily include the features we need.これらの一部は次のとおりです。

  • RxJavaおよびKotlinのコルーチンサポート用のモジュール

  • AndroidおよびAndroid LiveDataアーキテクチャコンポーネントサポート用のモジュール

  • 使用するオブジェクトシリアル化モジュールを選択できる4つのモジュール-Gson、Jackson、Moshi、またはForge。

このチュートリアルでは、コアモジュール、コルーチンのモジュール、RxJava、およびGsonシリアル化モジュールに焦点を当てます。


    com.github.kittinunf.fuel
    fuel
    ${fuel.version}


    com.github.kittinunf.fuel
    fuel-gson
    ${fuel.version}


    com.github.kittinunf.fuel
    fuel-rxjava
    ${fuel.version}


    com.github.kittinunf.fuel
    fuel-coroutines
    ${fuel.version}

JFrog Bintrayで最新バージョンを見つけることができます。

3. リクエストする

To make a request, Fuel provides a String extension.さらに、代わりに、各HTTP動詞のメソッドを持つFuelクラスを使用できます。

Fuelは、PATCHを除くすべてのHTTP動詞をサポートしています。 その理由は、Fuel’s HttpClient is a wrapper over java.net.HttpUrlConnection which doesn’t support PATCH.

この問題を回避するために、HttpClientはPATCHリクエストをPOSTリクエストに変換し、X-HTTP-Method-Override: PATCHヘッダーを追加するため、APIがデフォルトでこのヘッダーを受け入れるように構成されていることを確認する必要があります。

Fuelの機能を説明するために、単純なHTTP要求と応答サービスであるhttpbin.orgと、テストとプロトタイピング用の偽のオンラインAPIであるJsonPlaceholderを使用します。

3.1. GETリクエスト

非同期モードで単純なHTTPGETリクエストの作成を始めましょう。

"http://httpbin.org/get".httpGet().response {
  request, response, result ->
    //response handling
}

StringよりもhttpGet()を使用すると、Triple<Request, Response, Result>が得られます。

TheResultis a functional-style data structure that contains the result of the operation (success or failure). 後の段階でResultのデータ構造を再検討します。

ブロッキングモードでリクエストを行うこともできます。

val (request, response, result) = "http://httpbin.org/get"
  .httpGet().response()

返されるパラメーターは非同期バージョンと同じですが、この場合、リクエストを行ったスレッドはブロックされます。

また、エンコードされたURLパラメータを使用する可能性があります。

val (request, response, result) =
  "https://jsonplaceholder.typicode.com/posts"
  .httpGet(listOf("userId" to "1")).response()
  // resolve to https://jsonplaceholder.typicode.com/posts?userId=1

httpGet()メソッド(および他の同様のメソッド)は、URLパラメータをエンコードするためにList<Pair>を受け取ることができます。

3.2. POSTリクエスト

GETの場合と同じ方法で、httpPost()を使用するか、Fuelクラスのpost()メソッドを使用してPOSTリクエストを行うことができます。

"http://httpbin.org/post".httpPost().response{
  request, response, result ->
    //response handling
}
val (request, response, result) = Fuel.post("http://httpbin.org/post")
  .response()

本体がある場合は、JSON文字列形式のbody()メソッドを使用できます。

val bodyJson = """
  { "title" : "foo",
    "body" : "bar",
    "id" : "1"
  }
"""
val (request, response, result) = Fuel.post("https://jsonplaceholder.typicode.com/posts")
  .body(bodyJson)
  .response()

3.3. 他の動詞

GETおよびPOSTと同様に、残りの動詞ごとにメソッドがあります。

Fuel.put("http://httpbin.org/put")
Fuel.delete("http://httpbin.org/delete")
Fuel.head("http://httpbin.org/get")
Fuel.patch("http://httpbin.org/patch")

Fuel.patch()X-HTTP-Method-Override: PATCHヘッダーを使用してPOSTリクエストを実行することに注意してください。

4. 設定

ライブラリは、グローバル構成を管理するためのシングルトンオブジェクト(FuelManager.instance)を提供します。

ベースパス、いくつかのヘッダー、および一般的なパラメータを構成しましょう。 また、いくつかのインターセプターを構成しましょう。

4.1. BasePath

basePath variableを使用して、すべてのリクエストに共通のパスを設定できます。

FuelManager.instance.basePath = "http://httpbin.org"
val (request, response, result) = "/get".httpGet().response()
// will perform GET http://httpbin.org/get

4.2. ヘッダ

さらに、baseHeadersマップを使用して一般的なHTTPヘッダーを管理できます。

FuelManager.instance.baseHeaders = mapOf("OS" to "Debian")

別の方法として、ローカルヘッダーを設定する場合は、リクエストでheader()メソッドを使用できます。

val (request, response, result) = "/get"
  .httpGet()
  .header(mapOf("OS" to "Debian"))
  .response()

4.3. パラメータ

最後に、baseParamsリストを使用して共通のパラメータを設定することもできます。

FuelManager.instance.baseParams = listOf("foo" to "bar")

4.4. 別のオプション

FuelManager:で管理できるオプションは他にもたくさんあります

  • デフォルトではnullであるkeystore 

  • socketFactoryは、ユーザーによって提供されるか、nullでない場合はkeystoreから派生します

  • HttpsURLConnectionクラスによって提供されるものを使用するようにデフォルトで設定されているhostnameVerifier

  • requestInterceptorsおよびresponseInterceptors

  • リクエストのtimeouttimeoutRead

4.5. Request/Response Interceptors

インターセプターに関しては、we can add supplied request/response interceptors like cUrlLoggingRequestInterceptors(), or we can define ours

FuelManager.instance.addRequestInterceptor(cUrlLoggingRequestInterceptor())
FuelManager.instance.addRequestInterceptor(tokenInterceptor())
fun tokenInterceptor() = {
    next: (Request) -> Request ->
    { req: Request ->
        req.header(mapOf("Authorization" to "Bearer AbCdEf123456"))
        next(req)
    }
}

5. 応答処理

以前、操作結果(成功または失敗)を表す機能データ構造(Result)を導入しました。

Resultの操作は簡単です。これは、ByteArrayString, JSON, 、または汎用のTオブジェクトに応答を含めることができるデータクラスです。

fun response(handler: (Request, Response, Result) -> Unit)
fun responseString(handler: (Request, Response, Result) -> Unit)
fun responseJson(handler: (Request, Response, Result) -> Unit)
fun  responseObject(deserializer: ResponseDeserializable,
  handler: (Request, Response, Result) -> Unit)

これを説明するために、Stringとして応答を取得しましょう。

val (request, response, result) = Fuel.post("http://httpbin.org/post")
  .responseString()
val (payload, error) = result // payload is a String

JSON形式の応答にはAndroidの依存関係が必要であることに注意してください。


    com.github.kittinunf.fuel
    fuel-android
    ${fuel.version}

6. JSONシリアル化/逆シリアル化

Fuelは、4つの方法で応答の逆シリアル化の組み込みサポートを提供します。これらの方法は、ニーズと選択したJSON解析ライブラリに応じて、以下を実装する必要があります。

public fun deserialize(bytes: ByteArray): T?
public fun deserialize(inputStream: InputStream): T?
public fun deserialize(reader: Reader): T?
public fun deserialize(content: String): T?

Gsonモジュールを含めることで、オブジェクトを逆シリアル化およびシリアル化できます。

data class Post(var userId:Int,
                var id:Int,
                var title:String,
                var body:String){

    class Deserializer : ResponseDeserializable> {
        override fun deserialize(content: String): Array
          = Gson().fromJson(content, Array::class.java)
    }
}

カスタムデシリアライザーでオブジェクトをデシリアライズできます:

"https://jsonplaceholder.typicode.com/posts"
  .httpGet().responseObject(Post.Deserializer()){
    _,_, result ->
      val postsArray = result.component1()
  }

または、内部Gsonデシリアライザーを使用するresponseObject を介して:

"https://jsonplaceholder.typicode.com/posts/1"
  .httpGet().responseObject { _, _, result ->
    val post = result.component1()
  }

一方、Gson().toJson()を使用してシリアル化できます。

val post = Post(1, 1, "Lorem", "Lorem Ipse dolor sit amet")

val (request, response, result)
  = Fuel.post("https://jsonplaceholder.typicode.com/posts")
    .header("Content-Type" to "application/json")
    .body(Gson().toJson(post).toString())

Content-Typeを設定することが重要です。そうしないと、サーバーが別のJSONオブジェクト内でオブジェクトを受信する可能性があります。

最終的に、同様の方法で、Jackson、Moshi、またはForgeの依存関係を使用してそれを行うことができます。

7. ファイルのダウンロードとアップロード

Fuelライブラリには、ファイルのダウンロードとアップロードに必要なすべての機能が含まれています。

7.1. ダウンロード

download()メソッドを使用すると、ファイルを簡単にダウンロードして、destination()ラムダ:によって返されるファイルに保存できます。

Fuel.download("http://httpbin.org/bytes/32768")
  .destination { response, url ->
    File.createTempFile("temp", ".tmp")
  }

We can also download a file with a progress handler

Fuel.download("http://httpbin.org/bytes/327680")
  .progress { readBytes, totalBytes ->
    val progress = readBytes.toFloat() / totalBytes.toFloat()
    //...
  }

7.2. アップロードする

同様に、we can upload a file using upload() method,は、source()メソッドでアップロードするファイルを示します。

Fuel.upload("/upload").source { request, url ->
  File.createTempFile("temp", ".tmp")
}

upload()はデフォルトでPOST動詞を使用することに注意してください。 別のHTTP動詞を使用する場合は、指定できます。

Fuel.upload("/upload", Method.PUT).source { request, url ->
  File.createTempFile("temp", ".tmp")
}

さらに、ファイルのリストを受け入れるsources()メソッドを使用して、複数のファイルをアップロードできます。

Fuel.upload("/post").sources { request, url ->
  listOf(
    File.createTempFile("temp1", ".tmp"),
    File.createTempFile("temp2", ".tmp")
  )
}

最後に、InputStream:からデータのブロブをアップロードできます

Fuel.upload("/post").blob { request, url ->
  Blob("filename.png", someObject.length, { someObject.getInputStream() })
}

8. RxJavaとコルーチンのサポート

Fuelは、非同期の非ブロッキングコードを記述する2つの方法であるRxJavaとコルーチンのサポートを提供します。

RxJavaは、非同期およびイベントベースのプログラムを作成するためのライブラリであるReactive ExtensionsのJavaVM実装です。

Observer patternを拡張してデータ/イベントのシーケンスをサポートし、同期、スレッドセーフ、同時データ構造を気にすることなく、宣言的にシーケンスを作成できる演算子を追加します。

Kotlin’s Coroutinesは軽量スレッドのようなものであるため、並行して実行し、相互に待機して通信することができます。最大の違いは、コルーチンが非常に安価であるということです。私たちは何千ものそれらを作成することができ、メモリの面でほとんど支払うことができません。

8.1. RxJava

RxJava 2.xをサポートするために、Fuelは6つの拡張機能を提供します。

fun Request.rx_response(): Single>>
fun Request.rx_responseString(charset: Charset): Single>>
fun  Request.rx_responseObject(deserializable: Deserializable):
  Single>>
fun Request.rx_data(): Single>
fun Request.rx_string(charset: Charset): Single>
fun  Request.rx_object(deserializable: Deserializable): Single>

すべての異なる応答タイプをサポートするために、各メソッドは異なるSingle<Result<>>.を返すことに注意してください

Requestを介してより関連性の高いメソッドを呼び出すことにより、「Rx」メソッドを簡単に使用できます。

 "https://jsonplaceholder.typicode.com/posts?id=1"
  .httpGet().rx_object(Post.Deserializer()).subscribe{
    res, throwable ->
      val post = res.component1()
  }

8.2. コルーチン

コルーチンモジュールでは、Fuel provides extension functions to wrap a response inside a coroutine and handle its result.

コルーチンを使用するために、同様のAPIが利用可能になります。たとえば、responseString()awaitStringResponse():になりました

runBlocking {
    Fuel.get("http://httpbin.org/get").awaitStringResponse()
}

また、awaitObject(), awaitObjectResult() or awaitObjectResponse():を使用してStringまたはByteArray awaitByteArrayResponse())以外のオブジェクトを処理するための便利なメソッドも提供します

runBlocking {
    Fuel.get("https://jsonplaceholder.typicode.com/posts?id=1")
      .awaitObjectResult(Post.Deserializer())
}

Kotlinのコルーチンは実験的なものであることに注意してください。つまり、今後のリリースで変更される可能性があります。

9. APIルーティング

最後になりましたが、ネットワークルートを処理するために、Fuelはルーターデザインパターンを実装することでサポートを提供します。

ルーターパターンを使用すると、FuelRoutingインターフェイスを使用してAPIの管理を一元化できます。これにより、呼び出されたエンドポイントに応じて適切なHTTP動詞、パス、パラメーター、ヘッダーを設定するためのメソッドの組み合わせが提供されます。

インターフェースは、ルーターを構成するための5つのプロパティを定義します。

sealed class PostRoutingAPI : FuelRouting {
    class posts(val userId: String, override val body: String?): PostRoutingAPI()
    class comments(val postId: String, override val body: String?): PostRoutingAPI()
    override val basePath = "https://jsonplaceholder.typicode.com"

    override val method: Method
        get() {
            return when(this) {
                is PostRoutingAPI.posts -> Method.GET
                is PostRoutingAPI.comments -> Method.GET
            }
        }

    override val path: String
        get() {
            return when(this) {
                is PostRoutingAPI.posts -> "/posts"
                is PostRoutingAPI.comments -> "/comments"
            }
        }

    override val params: List>?
        get() {
            return when(this) {
                is PostRoutingAPI.posts -> listOf("userId" to this.userId)
                is PostRoutingAPI.comments -> listOf("postId" to this.postId)
            }
        }

    override val headers: Map?
        get() {
            return null
        }
}

使用するHTTP動詞を選択するために、methodプロパティがあります。同様に、pathプロパティをオーバーライドして、適切なパスを選択できます。

さらに、paramsプロパティを使用すると、リクエストのパラメータを設定する機会があります。HTTPヘッダーを設定する必要がある場合は、関連するプロパティをオーバーライドできます。

したがって、request()メソッドを使用してチュートリアル全体で行ったのと同じ方法で使用します。

Fuel.request(PostRoutingAPI.posts("1",null))
  .responseObject(Post.Deserializer()) {
      request, response, result ->
        //response handling
  }
Fuel.request(PostRoutingAPI.comments("1",null))
  .responseString { request, response, result ->
      //response handling
  }

10. 結論

この記事では、Kotlin用のFuel HTTPライブラリと、あらゆるユースケースでのより便利な機能を紹介しました。

ライブラリは常に進化しているため、新しい機能を追跡するために、GitHubのリポジトリを確認してください。

いつものように、チュートリアルで言及されているすべてのコードスニペットは、GitHub repositoryにあります。