Brandstof voor HTTP-bibliotheek met Kotlin

1. Overzicht

In deze zelfstudie bekijken we de Fuel HTTP-bibliotheek, wat, in de woorden van de auteur, de gemakkelijkste HTTP-netwerkbibliotheek voor Kotlin / Android is. Bovendien kan de bibliotheek ook in Java worden gebruikt.

De belangrijkste kenmerken van de bibliotheek zijn:

  • Ondersteuning voor standaard HTTP-werkwoorden (GET, POST, DELETE, etc.) zowel asynchrone als blokkeringsverzoeken
  • Mogelijkheid om een ​​bestand te downloaden en te uploaden (multipart / form-data)
  • Mogelijkheid om globale configuratie te beheren
  • Ingebouwde objectserialisatiemodules (Jackson, Gson, Mhosi, Forge)
  • Ondersteuning voor de coroutines-module van Kotlin en RxJava 2.x
  • Stel het ontwerppatroon van de router eenvoudig in

2. Afhankelijkheden

De bibliotheek is samengesteld uit verschillende modules, zodat we de functies die we nodig hebben gemakkelijk kunnen opnemen. Enkele hiervan zijn:

  • Een module voor ondersteuning van RxJava en Kotlin's Coroutines
  • Een module voor ondersteuning van Android en Android LiveData Architecture Components
  • Vier modules waaruit we de te gebruiken objectserialisatiemodule kunnen kiezen - Gson, Jackson, Moshi of Forge.

In deze tutorial zullen we ons concentreren op de kernmodule, de modules voor Coroutines, RxJava en de Gson-serialisatiemodule:

 com.github.kittinunf.fuel brandstof $ {fuel.version} com.github.kittinunf.fuel brandstof-gson $ {fuel.version} com.github.kittinunf.fuel brandstof-rxjava $ {fuel.version} com.github. kittinunf.fuel fuel-coroutines $ {fuel.version} 

U kunt de nieuwste versies vinden op JFrog Bintray.

3. Verzoeken doen

Om een ​​verzoek in te dienen, biedt Fuel een Draad uitbreiding. Daarnaast en als alternatief kunnen we de Brandstof klasse die een methode heeft voor elk HTTP-werkwoord.

Fuel ondersteunt alle HTTP-werkwoorden behalve PATCH. De reden is dat Brandstof HttpClient is een wikkel voorbij java.net.HttpUrlConnection die PATCH niet ondersteunt.

Om het probleem te omzeilen, converteert de HttpClient PATCH-verzoeken naar een POST-verzoek en voegt een X-HTTP-Method-Override: PATCH header, dus we moeten ervoor zorgen dat onze API's zijn geconfigureerd om deze header standaard te accepteren.

Om de functies van Fuel uit te leggen, gaan we httpbin.org gebruiken, een eenvoudige HTTP-verzoek- en antwoordservice, en JsonPlaceholder - een nep-online API voor testen en prototypen.

3.1. KRIJG Verzoek

Laten we beginnen met het maken van eenvoudige HTTP KRIJGEN verzoek in asynchrone modus:

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

Gebruik makend van httpGet () over een Draad geeft ons een Verdrievoudigen.

De Resultaat is een datastructuur in functionele stijl die het resultaat van de bewerking bevat (succes of mislukking). We komen terug Resultaat datastructuur in een later stadium.

We kunnen het verzoek ook indienen in de blokkeermodus:

val (verzoek, antwoord, resultaat) = "//httpbin.org/get" .httpGet (). response ()

Merk op dat de geretourneerde parameters hetzelfde zijn als de asynchrone versie, maar in dit geval wordt de thread die het verzoek heeft gedaan, geblokkeerd.

Er is ook een mogelijkheid om gecodeerde URL-parameters te gebruiken:

val (request, response, result) = "//jsonplaceholder.typicode.com/posts" .httpGet (listOf ("userId" to "1")). response () // resolven naar //jsonplaceholder.typicode.com/ berichten? userId = 1 

De httpGet () methode (en de andere soortgelijke) kunnen een Lijst om URL-parameters te coderen.

3.2. POST-verzoek

We kunnen POST-verzoeken op dezelfde manier doen als voor GET, met behulp van httpPost () of met behulp van de post() methode van de Brandstof klasse:

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

Als we een lichaam hebben, kunnen we het erdoorheen halen lichaam() methode in JSON-tekenreeksindeling:

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

3.3. Andere werkwoorden

Hetzelfde als voor GET en POST, er is een methode voor elk van de overige werkwoorden:

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

Onthoud dat Fuel.patch () zal een POST-verzoek uitvoeren met eenX-HTTP-Method-Override: PATCH koptekst.

4. Configuratie

De bibliotheek levert een singleton-object - FuelManager.instance - om de globale configuratie te beheren.

Laten we een basispad, enkele headers en algemene parameters configureren. Laten we ook enkele interceptors configureren.

4.1. BasePath

Gebruik makend van basePath variabele kunnen we een gemeenschappelijk pad instellen voor alle verzoeken.

FuelManager.instance.basePath = "//httpbin.org" val (request, response, result) = "/get".httpGet (). Response () // zal GET //httpbin.org/get uitvoeren

4.2. Headers

Bovendien kunnen we algemene HTTP-headers beheren met behulp van baseHeaders kaart:

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

Op een alternatieve manier, als we een lokale header willen instellen, kunnen we de koptekst () methode op het verzoek:

val (verzoek, antwoord, resultaat) = "/ get" .httpGet () .header (mapOf ("OS" naar "Debian")) .response ()

4.3. Params

Ten slotte kunnen we ook algemene parameters instellen met behulp van de baseParams lijst:

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

4.4. Andere opties

Er zijn veel meer opties die we kunnen beheren Brandstofmanager:

  • keystore dat is nul standaard
  • socketFactory die zal worden verstrekt door de gebruiker of waarvan is afgeleid keystore als het niet is nul
  • hostnameVerifier die standaard is ingesteld om degene te gebruiken die wordt geboden door HttpsURLConnection klasse
  • requestInterceptors en reactie Interceptors
  • time-out en timeoutRead voor een verzoek

4.5. Interceptors van verzoek / antwoord

Met betrekking tot onderscheppers, we kunnen aangeleverde verzoek- / antwoordinterceptors toevoegen, zoals cUrlLoggingRequestInterceptors (), of we kunnen de onze definiëren:

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

5. Afhandeling van reacties

Eerder introduceerden we een functionele datastructuur - Resultaat - dat staat voor het resultaat van de operatie (succes of mislukking).

Werken met Resultaat is eenvoudig, het is een dataklasse die het antwoord kan bevatten in ByteArray, Tekenreeks, JSON, of een generiek T voorwerp:

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

Laten we een antwoord krijgen als een Draad om dit te illustreren:

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

Merk op dat het antwoord in JSON-indeling Android-afhankelijkheden vereist.

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

6. JSON-serialisering / deserialisering

Fuel biedt ingebouwde ondersteuning voor respons-deserialisatie met vier methoden die, afhankelijk van onze behoeften en van de JSON-parseerbibliotheek die we kiezen, moeten worden geïmplementeerd:

openbaar plezier deserialiseren (bytes: ByteArray): T? publiek plezier deserialiseren (inputStream: InputStream): T? publiek plezier deserialiseren (lezer: lezer): T? publiek plezier deserialiseren (inhoud: String): T?

Door de Gson-module op te nemen, kunnen we objecten deserialiseren en serialiseren:

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

We kunnen objecten deserialiseren met een aangepaste deserialisator:

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

Of via responseObject dat interne Gson-deserializer gebruikt:

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

Aan de andere kant kunnen we serialiseren met Gson (). ToJson ():

val post = Post (1, 1, "Lorem", "Lorem Ipse dolor sit amet") val (request, response, result) = Fuel.post ("// jsonplaceholder.typicode.com/posts") .header (" Inhoudstype "to" application / json ") .body (Gson (). ToJson (post) .toString ())

Het is belangrijk om de Inhoudstypeanders kan de server het object binnen een ander JSON-object ontvangen.

Uiteindelijk kunnen we het op een vergelijkbare manier doen door afhankelijkheden van Jackson, Moshi of Forge te gebruiken.

7. Download en upload bestand

De Fuel-bibliotheek bevat alle noodzakelijke functies om bestanden te downloaden en te uploaden.

7.1. Downloaden

Met de downloaden () methode kunnen we eenvoudig een bestand downloaden en opslaan in het bestand dat wordt geretourneerd door de bestemming() lambda:

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

We kunnen ook een bestand downloaden met een voortgangshandler:

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

7.2. Uploaden

Op dezelfde manier, we kunnen een bestand uploaden met uploaden() methode, het aangeven van het bestand dat moet worden geüpload met de bron() methode:

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

Let daar op uploaden() gebruikt standaard het POST-werkwoord. Als we een ander HTTP-werkwoord willen gebruiken, kunnen we dit specificeren:

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

Bovendien kunnen we meerdere bestanden uploaden met bronnen () methode die een lijst met bestanden accepteert:

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

Ten slotte kunnen we een blob met gegevens uploaden van een Invoerstroom:

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

8. Ondersteuning voor RxJava en Coroutines

Fuel biedt ondersteuning voor RxJava en Coroutines, twee manieren om asyncrhonus, niet-blokkerende code te schrijven.

RxJava is een Java VM-implementatie van Reactive Extensions, een bibliotheek voor het samenstellen van asynchrone en op gebeurtenissen gebaseerde programma's.

Het breidt het Observer-patroon uit om reeksen gegevens / gebeurtenissen te ondersteunen en voegt operators toe die het declaratief samenstellen van reeksen mogelijk maken zonder dat u zich zorgen hoeft te maken over synchronisatie, thread-veiligheid en gelijktijdige datastructuren.

De Coroutines van Kotlin zijn als lichtgewicht draden en als zodanig kunnen ze parallel lopen, op elkaar wachten en communiceren ... Het grootste verschil is dat coroutines erg goedkoop zijn; we kunnen er duizenden maken, en we betalen heel weinig in termen van geheugen.

8.1. RxJava

Om RxJava 2.x te ondersteunen, biedt Fuel zes extensies:

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

Merk op dat, om alle verschillende antwoordtypen te ondersteunen, elke methode een andere retourneert Single.

We kunnen gemakkelijk "Rx" -methoden gebruiken door de meer relevante aan te roepen boven een Verzoek:

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

8.2. Coroutines

Met de coroutines-module, Fuel biedt uitbreidingsfuncties om een ​​reactie in een coroutine te wikkelen en het resultaat ervan te verwerken.

Om Coroutines te gebruiken, worden vergelijkbare API's beschikbaar gesteld, bijv responseString () werd awaitStringResponse ():

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

Het biedt ook handige methoden voor het omgaan met andere objecten dan Draad of ByteArray (awaitByteArrayResponse ()) gebruik makend van awaitObject (), awaitObjectResult () of awaitObjectResponse ():

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

Onthoud dat Kotlin's Coroutines experimenteel zijn, wat betekent dat het in de komende releases kan worden gewijzigd.

9. API-routering

Last but not least, om netwerkroutes af te handelen, biedt Fuel de ondersteuning door het Router-ontwerppatroon te implementeren.

Met het routerpatroon kunnen we het beheer van de API centraliseren met behulp van de FuelRouting interface, die een combinatie van methoden biedt voor het instellen van het juiste HTTP-werkwoord, pad, params en headers volgens het aangeroepen eindpunt.

De interface definieert vijf eigenschappen waarmee het mogelijk is om onze router te configureren:

verzegelde klasse 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 = "/ /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"}} valparameters overschrijven: lijst? get () {retourneren wanneer (dit) {PostRoutingAPI.posts is -> listOf ("userId" naar this.userId) is PostRoutingAPI.comments -> listOf ("postId" to this.postId)}} val headers overschrijven: Map? get () {return null}}

Om te kiezen welk HTTP-werkwoord we moeten gebruiken methode eigenschap, kunnen we ook de pad eigenschap, om het juiste pad te kiezen.

Nog meer met de params eigenschap, hebben we de mogelijkheid om de parameters van het verzoek in te stellen en als we HTTP-headers moeten instellen, kunnen we dit doen door de betreffende eigenschap te overschrijven.

Daarom gebruiken we het op dezelfde manier als in de hele tutorial met de verzoek() methode:

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

In dit artikel hebben we de Fuel HTTP-bibliotheek voor Kotlin en de meer handige functies voor elk gebruik getoond.

De bibliotheek is constant in ontwikkeling, kijk daarom eens naar hun GitHub-repo - om nieuwe functies bij te houden.

Zoals gewoonlijk zijn alle codefragmenten die in de tutorial worden genoemd, te vinden in onze GitHub-repository.