Requests and Handlers

The basic async-http-client needs both a request and a handler in order to make the remote call. dispatch allows you to execute a bare request because it creates an "identity" handler for you automatically. An identity handler does not do anything other than return the underlying netty response object:

@ val responseFuture = Http(url("http://localhost:9000" )) 
responseFuture: Future[com.ning.http.client.Response] = Success(com.ning.http.client.providers.netty.response.NettyResponse@5d887ffc)
@ responseFuture onComplete  println 
Success(com.ning.http.client.providers.netty.response.NettyResponse@5d887ffc)

In the above, I created a GET request using the url() function and even though I did not have a handler defined, dispatch used the identity handler to issue the request. The return value is always a Future which I completed and printed out the body of the response. Since I was using the example http server, I got back the default response when accessing the root.

You can also create your own handler and execute the request with that. Dispatch provides help to create a handler. For example, it is very common to create a handler that allows a specific, expected response code to be recognized and if it is returned from the server, apply a function to the response body.

@ val responseFuture = Http(url("http://localhost:9000" ) OK as.String) 
responseFuture: Future[String] = List()
@ responseFuture onComplete  println 
Success(<h1>you have reached the example server</h1>)

This prints out the same thing as the first example of course because the phrase OK as.String uses dispatch to create a async-http-client AsyncHandler that first looks for a specific status code, OK (which is the status code 200), and then convert the body to a String object. The phrase as.String is a dispatch way of unmarshalling a response to a specific scala object. There are different unmarshallers available provided by dispatch to handle json or XML response bodies.

dispatch does not use an unmarshalling framework like akka http but keeps the unmarshaling concept very simple. In dispatch, a handler is really a function that converts a Response into some object of type T e.g. Response => T. OK in the example above is really a function that takes a "handler" function as an argument and returns a handler derived from async-http-client's AsyncHandler. dispatch defines a default "OK handler" that looks for a status code of 200 and if found, calls the handler function that follows the OK, in this case as.String. You could write OK as.String as OK(as.String).

The AsyncHandler has to implement the onStatusReceived and onComplete and other handler calls that the async-http-client's class requires. dispatch provides convenient ways to create these handlers. An AsyncHandler has multiple methods that must be satisfied and at some point either dispatch or yourself must implement these methods:

public interface AsyncHandler<T> {
    public void onThrowable(Throwable t)
    public STATE onBodyPartReceived(HttpResponseBodyPart bodyPart) throws Exception
    public STATE onStatusReceived(HttpResponseStatus responseStatus) throws Exception
    public STATE onHeadersReceived(HttpResponseHeaders headers) throws Exception
    public T onCompleted() throws Exception
}

Under the hood, when the dispatch level client is called upon to issue the request, the dispatch client creates a promise that is completed with the response passed back up from async-http-client. The promise is completed with the response value after running through your handler function and it is this promise that provides you a scala Future value to work with in scala. By importing Defaults._ a default execution context is created to execute the Future.

The newer async-http-client using jdk8 also allows future's to be used and while the syntax is not as expressive as in scala, you can get a java Future back so it seems that async-http-client is moving in the direction of the dispatch which is to be expected.

Last updated