Skip to content

REST Client

Edvin Syse edited this page Mar 2, 2016 · 18 revisions

WikiDocumentationREST Client

REST Client

Tornado FX comes with a REST Client built on top of Apache HttpClient 4. The actual HttpClient is not hidden from you, so you can use all the bells and whistles you are used to, but Tornado FX adds functionality spesific to REST communication in the form of extension functions.

If you don't use the REST Client in your application, feel free to exclude the httpclient and javax.json dependencies from your project descriptor.

Configuration

If you mostly access the same api on every call, you can set a base uri so subsequent calls only need to include relative urls. You can configure the base url anywhere you like, but the init function of your App class is a good place to do it.

class MyApp : App() {
    init {
        val api = find(Rest::class)
        api.baseURI = "http://contoso.com/api"
    }
}

Inject the Rest controller into your controllers in the [normal way](Dependency Injection).

Basic operations

There are convenience functions to perform GET, PUT, POST and DELETE operations.

class CustomerController : Controller() {
    val api = Rest by inject()

    fun loadCustomers(): ObservableList<Customer> = 
        api.get("customers").list().toModel()
}

CustomerController with loadCustomers call

So, what exactly is going on in the loadCustomers function? First we call api.get("customers") which will perform the call and return a HttpResponse object from the Apache HttpClient. We then call the extension function HttpResponse.list() which will consume the response and convert it to a javax.json.JsonArray. Lastly, we call the extension function JsonArray.toModel() which creates one Customer object per JsonObject in the array and calls JsonModel.updateModel on it. In this example, the type argument is taken from the function return type, but you could also write the above method like this if you prefer:

fun loadCustomers() = api.get("customers").list().toModel(Customer::class)

How you provide the type argument to the toModel function is a matter of taste, so choose the syntax you are most comfortable with.

With the exception of GET, these functions take an optional parameter with either a JSON object or a JsonModel object that will be the payload of your request, converted to a JSON string.

The following example updates a customer object.

fun updateCustomer(customer: Customer) = api.put("customers/${customer.id}", customer)

If the api endpoint returns the customer object to us after save, we would simply append a toModel call at the end of the function.

fun updateCustomer(customer: Customer) = api.put("customers/${customer.id}", customer)
    .toModel(Customer::class)

Error handling

If an I/O error occurs during the processing of the request, the default Error Handler will report the error to the user. You can ofcourse catch any errors yourself instead. To handle HTTP return codes, you might want to inspect the HttpResponse before you convert the result to JSON.

fun getCustomer(id: Int): Customer {
    val response = api.get("some/action")

    if (response.ok()) 
        return response.one().toModel() 
    else if (response.statusCode == 404)
        throw CustomerNotFound()
    else
        throw HttpError("getCustomer returned ${response.statusCode} ${response.reason}")
}

Extract status code and reason from HttpResponse

response.ok() is shorthand for response.statusCode == 200. You can also access any other property of the response in the normal way.

Authentication

Tornado FX makes it very easy to add basic authentication to your api requests:

api.setBasicAuth("username", "password")

You can also directly access the HttpClient by through api.client, or call api.configure()' where you can supply a function that acts upon the supplied HttpClientBuilder`. For advanced configuration, please see the Apache HttpClient documentation.

Intercept failed calls

You can for example show a login screen if an HTTP call fails with statusCode 401:

api.configure {
    it.addInterceptorLast { response: HttpResponse, context: HttpContext ->
        if (response.statusCode == 401)
            showLoginScreen("Invalid credentials, please log in again.")
    }
}

Connect to multiple API's

You can create multiple instances of the Rest class by subclassing it and configuring each subclass as you wish. Injection of subclasses work seamlessly.

Sequence numbers

If you do multiple http calls they will not be pooled and returned in the order you executed the calls. Any http request will return as soon as it is available. If you want to handle them in sequence, or even discard older results, you can use the HttpResponse.seq() function which will contain a Long sequence number.

Progress indicator

Tornado FX comes with a HTTP Client ProgressIndicator View. This view can be embedded in your application and will show you information about ongoing REST calls. Simply embed the RestProgressBar into a ToolBar or any other parent container:

toolbar.add(RestProgressBar::class)

Next: i18n

Clone this wiki locally