I want to implement a simple json REST server using spray in scala that supports the following routes:
GET /foo => return a list of case class objects in json format
POST /bar => read a json into a case class object and perform some computation
My basic starter code is the following:
import spray.routing.SimpleRoutingApp
import spray.can.Http
import akka.actor.ActorSystem
import akka.actor.Props
import akka.io.IO
import scala.collection.JavaConversions
import com.fasterxml.jackson.databind.ObjectMapper
object SprayTest extends App with SimpleRoutingApp {
implicit val system = ActorSystem("my-system")
val mapper = new ObjectMapper
case class Foo(a: String, b: Int)
case class Bar(c: Long, d: String)
startServer(interface = "localhost", port = 8080) {
get {
path("foo") {
complete {
val c = listOfFoo()
mapper.writeValueAsString(c)
}
}
} ~ post {
path("bar") {
val bar: Bar = ???
complete {
"???"
}
}
}
}
}
The two most important open issues with this code that I know of are:
I'm depending on jackson, but from searching the web, it seems like spray should have some sort of built in support for serializing and deserializing simple case objects or lists of case objects.
I'm not sure the "best", most idiomatic and succinct way to get content from the post request and marshall it into json so that I can perform some computations on the case class object(s)
Does anyone know the best approach? Is there a way to make the marshalling automatic, so I can execute something like complete { caseObject }
and have caseObject
convert automatically into json (and vice versa with defining POST method)?
Definitely use spray json. Usually you separate the data models into their own file:
import spray.json._
case class Foo(a: String, b: Int)
case class Bar(c: Long, d: String)
object FooBarJsonProtocol extends DefaultJsonProtocol{
implicit val fooFormat = jsonFormat2(Foo)
implicit val barFormat = jsonFormat2(Bar)
}
Then in the route
import FooBarJsonProtocol._
...
get {
path("foo") {
complete {
listOfFoo() //with the implicit in scope this will serialize as json
}
}
} ~ post {
path("bar") {
entity(as[Bar]) { bar => //extract json Bar from post body
complete(bar) //serialize bar to json (or whatever processing you want to do)
}
}
}
}
I can't imagine why this question was down-voted -- it seems specific and well-expressed.
It is a bit hard to find, but the Spray docs cover case class extraction under Spray Routing / Advanced Topics. It would be pointless to repeat their explanation here, but basically you want to use as[Foo]
to deserialize HTTP content into objects. The entity
directive can be used to do this for the body of a request, as shown in this longer example of the spray-routing DSL. mapTo
(used in the same example) is probably what you want to serialize an object for the response.
For JSON, it would probably be easiest to use their separate Spray-JSON library, since it plugs right into their typeclass mechanism, but I think you could with some effort marry up anything you want. Spray-JSON can handle a case class with one line of glue.
By the way, the line val bar: Bar = ???
in your sample code is going to be executed at the time the route is defined, not when a request comes in, as you presumably want. Please read the Understanding the DSL Structure section of the spray-routing docs.
Got it thanks, here is my answer
import spray.routing._
import spray.json._
import spray.httpx._
import spray.http._
case class UserLogin(username: String, password: String)
object UserLoginJsonSupport extends DefaultJsonProtocol with SprayJsonSupport {
implicit val PortofolioFormats = jsonFormat2(UserLogin)
}
import UserLoginJsonSupport._
trait UserAccountsServiceAPI extends HttpService{
val UserAccountsServiceRouting = {
path("api" / "userservice" ) {
post {
entity(as[UserLogin]) { userLogin =>
println("->"+userLogin.username)
println("->"+userLogin.password)
complete(userLogin.username)
}
}
}
}
}
My http request with curl app
curl -i 'http://localhost:8080/api/userservice' -X POST -H "Content-Type: application/json" -d '{"username": "admin", "password": "pwd"}'
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With