//if we set baseURL beforehand, simply use relativePath FuelManager.instance.basePath = "https://httpbin.org" "/get".httpGet().responseString { request, response, result -> //make a GET to https://httpbin.org/get and do something with response val (data, error) = result if (error == null) { //do something when success } else { //error handling } }
//if you prefer this a little longer way, you can always do //get Fuel.get("https://httpbin.org/get").responseString { request, response, result -> //do something with response result.fold({ d -> //do something with data }, { err -> //do something with error }) }
* Java
```java
//get
Fuel.get("https://httpbin.org/get", params).responseString(new Handler<String>() {
@Override
public void failure(Request request, Response response, FuelError error) {
//do something when it is failure
}
@Override
public void success(Request request, Response response, String data) {
//do something when it is successful
}
});
Blocking mode
You can also wait for the response. It returns the same parameters as the async version, but it blocks the thread. It supports all the features of the async version.
Kotlin
val (request, response, result) = "https://httpbin.org/get".httpGet().responseString() // result is Result<String, FuelError>
Java
```java try { Triple data = Fuel.get("https://www.google.com").responseString(); Request request = data.getFirst(); Response response = data.getSecond(); Result text = data.getThird(); } catch (Exception networkError) {
}
## Detail Usage
### GET
```kotlin
Fuel.get("https://httpbin.org/get").response { request, response, result ->
println(request)
println(response)
val (bytes, error) = result
if (bytes != null) {
println(bytes)
}
}
Response Handling
Result
Result is a functional style data structure that represents data that contains result of Success or Failure but not both. It represents the result of an action that can be success (with result) or error.
Working with result is easy. You could fold, destructure as because it is just a data class or do a simple when checking whether it is Success or Failure.
Fuel.post("https://httpbin.org/post").response { request, response, result ->}// JSON body from string (automatically sets application/json as Content-Type)Fuel.post("https://httpbin.org/post") .jsonBody("{ \"foo\" : \"bar\" }") .response { request, response, result -> }// Body from a generic stringFuel.post("https://httpbin.org/post") .header(Headers.CONTENT_TYPE, "text/plain") .body("my body is plain") .response { request, response, result -> }// Body from a fileFuel.post("https://httpbin.org/post") .header(Headers.CONTENT_TYPE, "text/plain") .body(File("lipsum.txt")) .response { request, response, result -> }// Body from a generic streamval stream =ByteArrayInputStream("source-string-from-string".toByteArray())Fuel.post("https://httpbin.org/post") .header(Headers.CONTENT_TYPE, "text/plain") .body(stream) .response { request, response, result -> }
PUT
Fuel.put("https://httpbin.org/put") .response { request, response, result -> }// Supports all the body methods, like POST requests
DELETE
Fuel.delete("https://httpbin.org/delete") .response { request, response, result -> }// Supports all the body methods, like POST requests
HEAD
Fuel.head("https://httpbin.org/get") .response { request, response, result ->/* request body is empty */ }
Fuel.patch("https://httpbin.org/patch").response { request, response, result -> }// Supports all the body methods, like POST requests
CONNECT
Connect is not supported by the Java JVM via the regular HTTP clients, and is therefore not supported.
OPTIONS
There are no convenience methods for making an OPTIONS request, but you can still make one directly:
Fuel.request(Method.OPTIONS, "https://httpbin.org/anything") .response { request, response, result -> }
TRACE
There are no convenience methods for making an TRACE request, but you can still make one directly:
Fuel.request(Method.TRACE, "https://httpbin.org/anything") .response { request, response, result -> }
Debug Logging
Use toString() method to inspect requests
val request = Fuel.get("https://httpbin.org/get", parameters =listOf("key" to "value"))println(request)// --> GET (https://httpbin.org/get?key=value)// Body : (empty)// Headers : (2)// Accept-Encoding : compress;q=0.5, gzip;q=1.0// Device : Android
Use toString() method to inspect responses
val (_, response, _) = Fuel.get("https://httpbin.org/get", parameters =listOf("key" to "value")).response()println(response)// <-- 200 (https://httpbin.org/get?key=value)// Body : (empty)
Also support cUrl string to Log request, make it very easy to cUrl on command line
val request = Fuel.post("https://httpbin.org/post", parameters = listOf("foo" to "foo", "bar" to "bar", "key" to "value"))
println(request.cUrlString())
Fuel.get("https://httpbin.org/get", listOf("foo" to "foo", "bar" to "bar")) .response { request, response, result -> }// resolve to https://httpbin.org/get?foo=foo&bar=barFuel.delete("https://httpbin.org/delete", listOf("foo" to "foo", "bar" to "bar")) .response { request, response, result -> }// resolve to https://httpbin.org/delete?foo=foo&bar=bar
Array support for GET requests
Fuel.get("https://httpbin.org/get", listOf("foo" to "foo", "dwarf" to arrayOf("grumpy","happy","sleepy","dopey"))) .response { request, response, result -> }// resolve to https://httpbin.org/get?foo=foo&dwarf[]=grumpy&dwarf[]=happy&dwarf[]=sleepy&dwarf[]=dopey
Support x-www-form-urlencoded for PUT & POST
Fuel.post("https://httpbin.org/post", listOf("foo" to "foo", "bar" to "bar")) .response { request, response, result -> }// Body : "foo=foo&bar=bar"Fuel.put("https://httpbin.org/put", listOf("foo" to "foo", "bar" to "bar")) .response { request, response, result -> }// Body : "foo=foo&bar=bar"
Set request's timeout and read timeout
Default timeout for a request is 15000 milliseconds. Default read timeout for a request is 15000 milliseconds.
Kotlin
```kotlin
val timeout = 5000 // 5000 milliseconds = 5 seconds.
val timeoutRead = 60000 // 60000 milliseconds = 1 minute.
Fuel.get("https://httpbin.org/get") .timeout(timeout) .timeoutRead(timeoutRead) .responseString { request, response, result -> }
* Java
```java
int timeout = 5000 // 5000 milliseconds = 5 seconds.
int timeoutRead = 60000 // 60000 milliseconds = 1 minute.
Fuel.get("https://httpbin.org/get", params).timeout(timeout).timeoutRead(timeoutRead).responseString(new Handler<String>() {
@Override
public void failure(Request request, Response response, FuelError error) {
//do something when it is failure
}
@Override
public void success(Request request, Response response, String data) {
//do something when it is successful
}
});
Fuel.upload("/post") .source { request, url -> File.createTempFile("temp", ".tmp") } .responseString { request, response, result -> }// By default upload use Method.POST, unless it is specified as something elseFuel.upload("/put", Method.PUT) .source { request, url -> File.createTempFile("temp", ".tmp") } .responseString { request, response, result -> }// Upload with multiple filesFuel.upload("/post") .sources { request, url ->listOf( File.createTempFile("temp1", ".tmp"), File.createTempFile("temp2", ".tmp") ) } .name { "temp" } .responseString { request, response, result -> }
Specify custom field names for files
Fuel.upload("/post") .dataParts { request, url ->listOf( //DataPart takes a file, and you can specify the name and/or typeDataPart(File.createTempFile("temp1", ".tmp"), "image/jpeg"), DataPart(File.createTempFile("temp2", ".tmp"), "file2"), DataPart(File.createTempFile("temp3", ".tmp"), "third-file", "image/jpeg") ) } .responseString { request, response, result ->/* ... */ }
Upload a multipart form without a file
val formData =listOf("Email" to "mail@example.com", "Name" to "Joe Smith" )Fuel.upload("/post", param = formData)// Upload normally requires a file, but we can give it an empty list of `DataPart` .dataParts { request, url ->listOf<DataPart>() } .responseString { request, response, result ->/* ... */ }
By default, the valid range for HTTP status code will be (200..299).
Cancel
If one wants to cancel on-going request, one could call cancel on the request object
val request = Fuel.get("https://httpbin.org/get") .response { request, response, result ->// if request is cancelled successfully, response callback will not be called. // Interrupt callback (if provided) will be called instead }//later request.cancel() //this will cancel on-going request
Also, interrupt request can be further processed with interrupt callback
val request = Fuel.get("https://httpbin.org/get") .interrupt { request ->println("${request.url} was interrupted and cancelled") } .response { request, response, result ->// if request is cancelled successfully, response callback will not be called.// Interrupt callback (if provided) will be called instead } request.cancel()
Advanced Configuration
Response Deserialization
Fuel provides built-in support for response deserialization. Here is how one might want to use Fuel together with Gson
```kotlin //User Model data class User(val firstName: String = "", val lastName: String = "") {
//User Deserializer class Deserializer : ResponseDeserializable { override fun deserialize(content: String) = Gson().fromJson(content, User::class.java) }
### Gson Deserialization
* Fuel also provides a built in support for Gson Deserialization. This is possible by including the [Gson](https://github.com/kittinunf/Fuel/tree/master/fuel-gson) module in your dependency block.
```kotlin
data class HttpBinUserAgentModel(var userAgent: String = "")
Fuel.get("/user-agent")
.responseObject<HttpBinUserAgentModel> { _, _, result -> }
It can be used with coroutines by using kotlinxDeserilaizerOf() it takes the same json and loader as parameters
@SerializabledataclassHttpBinUserAgentModel(var userAgent: String="")Fuel.get("/user-agent") .awaitResponseObject<HttpBinUserAgentModel>(kotlinxDeserializerOf()) { _, _, result -> }
There are 4 methods to support response deserialization depending on your needs (also depending on JSON parsing library of your choice), and you are required to implement only one of them.
Use singleton FuelManager.instance to manage global configurations.
basePath is used to manage common root path. Great usage is for your static API endpoint.
FuelManager.instance.basePath ="https://httpbin.org"// Later Fuel.get("/get").response { request, response, result -> //make request to https://httpbin.org/get because Fuel.{get|post|put|delete} use FuelManager.instance to make HTTP request
}
baseHeaders is to manage common HTTP header pairs in format of Map<String, String>.
The base headers are only applied if the request does not have those headers set.
FuelManager.instance.baseHeaders =mapOf("Device" to "Android")
Headers can be added to a request via various methods including
fun header(name: String, value: Any): Request: request.header("foo", "a")
fun header(pairs: Map<String, Any>): Request: request.header(mapOf("foo" to "a"))
fun header(vararg pairs: Pair<String, Any>): Request: request.header("foo" to "a")
operator fun set(header: String, value: Any): Request: request["foo"] = "a"
By default, all subsequent calls overwrite earlier calls, but you may use the appendHeader variant to append values to existing values.
In earlier versions a mapOf overwrote, and varargs pair did not, but this was confusing.
Some of the HTTP headers are defined under Headers.Companion and can be used instead of literal strings.
Fuel.post("/my-post-path") .header(Headers.ACCEPT, "text/html, */*; q=0.1") .header(Headers.CONTENT_TYPE, "image/png") .header(Headers.COOKIE to "basic=very") .appendHeader(Headers.COOKIE to "value_1=foo", Headers.COOKIE to "value_2=bar", Headers.ACCEPT to "application/json")
.appendHeader("MyFoo" to "bar", "MyFoo" to "baz") .response { /*...*/ }// => request with:// Headers:// Accept: "text/html, */*; q=0.1, application/json"// Content-Type: "image/png"// Cookie: "basic=very; value_1=foo; value_2=bar"// MyFoo: "bar, baz"
baseParams is used to manage common key=value query param, which will be automatically included in all of your subsequent requests in format of Parameters (Any is converted to String by toString() method)
FuelManager.instance.baseParams =listOf("api_key" to "1234567890")// Later Fuel.get("/get").response { request, response, result ->//make request to https://httpbin.org/get?api_key=1234567890 }
client is a raw HTTP client driver. Generally, it is responsible to make Request into Response. Default is HttpClient which is a thin wrapper over java.net.HttpUrlConnection. You could use any httpClient of your choice by conforming to client protocol, and set back to FuelManager.instance to kick off the effect.
keyStore is configurable by user. By default it is null.
socketFactory can be supplied by user. If keyStore is not null, socketFactory will be derived from it.
hostnameVerifier is configurable by user. By default, it uses HttpsURLConnection.getDefaultHostnameVerifier().
requestInterceptorsresponseInterceptors is a side-effect to add to Request and/or Response objects.
For example, one might wanna print cUrlString style for every request that hits server in DEBUG mode.
val manager =FuelManager()if (BUILD_DEBUG) { manager.addRequestInterceptor(cUrlLoggingRequestInterceptor()) } val (request, response, result) = manager.request(Method.GET, "https://httpbin.org/get").response() //it will print curl -i -H "Accept-Encoding:compress;q=0.5, gzip;q=1.0" "https://httpbin.org/get"
Another example is that you might wanna add data into your Database, you can achieve that with providing responseInterceptors such as
inlinefun <reifiedT> DbResponseInterceptor() = { next: (Request, Response) -> Response -> { req: Request, res: Response ->val db = DB.getInstance()val instance = Parser.getInstance().parse(res.data, T::class) db.transaction { it.copyToDB(instance) }next(req, res) } } manager.addResponseInterceptor(DBResponseInterceptor<Dog>) manager.request(Method.GET, "https://www.example.com/api/dog/1").response() // Db interceptor will be called to intercept data and save into Database of your choice
Test mode
Testing asynchronized calls can be somehow hard without special care. That's why Fuel has a special test mode with make all the requests blocking, for tests.
Fuel.testMode {
timeout = 15000 // Optional feature, set all requests' timeout to this value.
}
In order to disable test mode, just call Fuel.regularMode()
Fuel.get("www.example.com/get")
.liveDataResponse()
.observe(this) { /* do something */ }
Routing Support
In order to organize better your network stack FuelRouting interface allows you to easily setup a Router design pattern.
sealed class WeatherApi: FuelRouting {
override val basePath = "https://www.metaweather.com"
class weatherFor(val location: String): WeatherApi() {}
override val method: Method
get() {
when(this) {
is weatherFor -> return Method.GET
}
}
override val path: String
get() {
return when(this) {
is weatherFor -> "/api/location/search/"
}
}
override val params: Parameters?
get() {
return when(this) {
is weatherFor -> listOf("query" to this.location)
}
}
override val headers: Map<String, String>?
get() {
return null
}
}
// Usage
Fuel.request(WeatherApi.weatherFor("london"))
.responseJson { request, response, result ->
result.fold(success = { json ->
Log.d("qdp success", json.array().toString())
}, failure = { error ->
Log.e("qdp error", error.toString())
})
}
Coroutines Support
Coroutines module provides extension functions to wrap a response inside a coroutine and handle its result. The coroutines-based API provides equivalent methods to the standard API (e.g: responseString() in coroutines is awaitStringResponse()).
runBlocking {
val (request, response, result) = Fuel.get("https://httpbin.org/ip").awaitStringResponse()
result.fold(
{ data -> println(data) /* "{"origin":"127.0.0.1"}" */ },
{ error -> println("An error of type ${error.exception} happened: ${error.message}") }
)
}
There are functions to handle Result object directly too.
runBlocking {
Fuel.get("https://httpbin.org/ip")
.awaitStringResult()
.fold(
{ data -> println(data) /* "{"origin":"127.0.0.1"}" */ },
{ error -> println("An error of type ${error.exception} happened: ${error.message}") }
)
}
It also provides useful methods to retrieve the ByteArray,String or Object directly. The difference with these implementations is that they throw exception instead of returning it wrapped a FuelError instance.
Handling objects other than String (awaitStringResponse()) or ByteArray (awaitByteArrayResponse()) can be done using awaitObject, awaitObjectResult or awaitObjectResponse.
data class Ip(val origin: String)
object IpDeserializer : ResponseDeserializable<Ip> {
override fun deserialize(content: String) =
jacksonObjectMapper().readValue<Ip>(content)
}
runBlocking {
Fuel.get("https://httpbin.org/ip")
.awaitObjectResult(IpDeserializer)
.fold(
{ data -> println(data.origin) /* 127.0.0.1 */ },
{ error -> println("An error of type ${error.exception} happened: ${error.message}") }
)
}
runBlocking {
try {
val data = Fuel.get("https://httpbin.org/ip").awaitObject(IpDeserializer)
println(data.origin) // 127.0.0.1
} catch (exception: Exception) {
when (exception){
is HttpException -> println("A network request exception was thrown: ${exception.message}")
is JsonMappingException -> println("A serialization/deserialization exception was thrown: ${exception.message}")
else -> println("An exception [${exception.javaClass.simpleName}\"] was thrown")
}
}
}
Project Reactor
The Reactor module API provides functions starting with the prefix mono to handle instances of Response, Result<T, FuelError> and values directly (String, ByteArray, Any). All functions expose exceptions as FuelError instance.
Data handling example
Fuel.get("https://icanhazdadjoke.com")
.header(Headers.ACCEPT to "text/plain")
.monoString()
.subscribe(::println)
Error handling example
data class Guest(val name: String)
object GuestMapper : ResponseDeserializable<Guest> {
override fun deserialize(content: String) =
jacksonObjectMapper().readValue<Guest>(content)
}
Fuel.get("/guestName").monoResultObject(GuestMapper)
.map(Result<Guest, FuelError>::get)
.map { (name) -> "Welcome to the party, $name!" }
.onErrorReturn("I'm sorry, your name is not on the list.")
.subscribe(::println)