Just Handlers
Just Handlers
If you issue a request and receive a response, you need to create a handler. dispatch provides some handy handlers to convert the response body to a string, XML or using whatever function you have that converts a request to an output value.
Some of the as.*
that you have out of the box include:
as.Response
: Returns the raw response object.as.String.utf8
: Returns utf8 string.as.Bytes
: Returns bytes.as.File(yourfile)
: Redirects the body to a file.as.XML
: Creates XML a scala scala.xml.Elem.
You can also pass in your own function that handles a full async-http-client Response and returns some value derived from it. For example, the response body is available as Reseponse.getResponseBody
so you could do:
@ Http(host("localhost:9000") > { _.getResponseBody} ) onComplete println
Success(<h1>you have reached the example server</h1>)
which does the same thing as as.String
but illustrates the syntax somewhat. You could also define your function elsewhere and use it instead of {_.getResponseBody}
. You should notice that this function only runs after the entire response has been received so you lose as-you-get-it processing capabilities using this approach. You can also provide your own AsyncHandler
directly and use >
.
Generally, you will wan to use OK
and your handler, a function Response => T
so that your function only runs if the call was successful:
@ Http(host("localhost:9000") OK { _.getResponseBody} ) onComplete println
Success(<h1>you have reached the example server</h1>)
@ Http(host("localhost:9000") OK { _.getStatusCode} ) onComplete println
Success(200)
The >
function and AsyncHandler
>
function and AsyncHandler
The >
function to create a handler is overloaded. You can provide a function, like we did in the last section, or an AsyncHandler
.
There is also a handler at at.stream.Lines
that takes a String => T
function parameter that processes each line of response using the function parameter. If we alter our example-server program to return a well known set of lines:
} ~ path("lines") {
get {
complete("1\n2\n3\n")
}
}
then we can use it like:
@ Http(host("localhost:9000") / "lines" > as.stream.Lines(println)) onComplete println
1
2
3
Success(())
Using the handler this way is clearly being used for its side-effects. We also used the >
method which directly takes an async-http-client AsyncHandler
directly. When you want explicit control over response processing you would write your own AsyncHandler
. You would use this, for example, to interface to a processing library like monix.
json response handling
XML is a bit deprecated today and the new hotness, for the past decade actually, has really been json.
dispatch has json support for a few libarries but does not restrict you to only using them. Everyone uses a different json library it seems.
First, we need to alter our example server to return some json, the route will add is:
~ path("json") {
get {
complete("""{ "result" : "hello world"}""")
}
}
There are a few add-ons for json support provided by dispatch. We will show the one based on lift json, which has not been updated in a long time, but still works of course:
load.ivy("net.databinder.dispatch" %% "dispatch-core" % "0.11.3")
load.ivy("net.databinder.dispatch" %% "dispatch-lift-json" % "0.11.3")
load.ivy("net.liftweb" %% "lift-json" % "latest.release")
import scala.async.Async._
import dispatch._, Defaults._
import scala.concurrent.Await
import scala.concurrent.duration._
import net.liftweb.json._
import JsonDSL._
def callit() = Http(url(s"http://localhost:9000/json") OK as.lift.Json)
callit() onComplete { r => println(s"Result $r")}
You call the handle just like the other handlers, as.lift.Json
and get back a json object:
@ callit() onComplete { r => println(s"Result $r")}
Result Success(JObject(List(JField(result,JString(hello world)))))
The code for the handler suggests how simple it is to create your own json handler:
object Json extends (Response => JValue) {
def apply(r: Response) =
(dispatch.as.String andThen JsonParser.parse)(r)
}
You can see that using a combinator of andThen
you can piece together the handlers and build on previous handlers. First as.String
reads the response body and converts it to a String then JsonParser.parse
parses that result. You can do this because a handler is defined as a function and you can compose functions.
The circe library is the new hotness for json parsing, based on argonaut but faster (here on github). We just need a "handler" that takes a response to a circe json object, which is based on a cats library object since circe uses cats underneath:
load.ivy("net.databinder.dispatch" %% "dispatch-core" % "0.11.3")
Seq("io.circe" %% "circe-core",
"io.circe" %% "circe-generic",
"io.circe" %% "circe-parser") map (_ % "latest.release") foreach(load.ivy(_))
import dispatch._, Defaults._
import scala.concurrent.Await
import scala.concurrent.duration._
import com.ning.http.client.Response
import io.circe._, io.circe.generic.auto._, io.circe.parser._, io.circe.syntax._
import io.circe.{ Decoder, Encoder, Json }
import cats.data._
object CirceJson extends (Response => cats.data.Xor[ParsingFailure, Json]) {
def apply(r: Response) =
(dispatch.as.String andThen parse)(r)
}
def callit() = Http(url(s"http://localhost:9000/json") OK CirceJson)
callit() onComplete { r => println(s"Result $r")}
Which prints out:
Result Success(Right({
"result" : "hello world"
}))
the result we expect.
Last updated
Was this helpful?