/ / Scrivi un semplice server JSON REST usando spray in scala-json, scala, spray

Scrivi un semplice server JSON REST usando spray in scala-json, scala, spray

Voglio implementare un semplice server JSON REST usando spray in scala che supporti i seguenti percorsi:

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

Il mio codice base di base è il seguente:

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 {
"???"
}
}
}
}
}

I due problemi aperti più importanti con questo codice che conosco sono:

  1. Sono dipendente da Jackson, ma dalla ricerca sul web, sembra che lo spray dovrebbe avere una sorta di supporto integrato per la serializzazione e deserializzazione di semplici case o elenchi di oggetti del caso.

  2. Non sono sicuro che il "migliore", il modo più idiomatico e succinto di ottenere il contenuto dalla richiesta di post e lo porti in json in modo che possa eseguire alcuni calcoli sugli oggetti della classe del caso

Qualcuno conosce l'approccio migliore? C'è un modo per rendere automatico il marshalling, così posso eseguire qualcosa di simile complete { caseObject } e avere caseObject convertire automaticamente in json (e viceversa con la definizione del metodo POST)?

risposte:

20 per risposta № 1

Sicuramente usare spray json. Di solito si separano i modelli di dati nel proprio 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)
}

Quindi nel percorso

    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)
}
}
}
}

7 per risposta № 2

Non riesco a immaginare perché questa domanda sia stata messa sottosopra - sembra specifica e ben espressa.

È un po 'difficile da trovare, ma i documenti di Spray coprono estrazione della classe del case sotto Spray Routing / Advanced Topics. Sarebbe inutile ripetere qui la loro spiegazione, ma in pratica si vuole usare as[Foo] deserializzare il contenuto HTTP in oggetti. Il entity la direttiva può essere usata per fare questo per il corpo di una richiesta, come mostrato in questo esempio più lungo del DSL spray-routing. mapTo (utilizzato nello stesso esempio) è probabilmente ciò che si desidera serializzare un oggetto per la risposta.

Per JSON, sarebbe probabilmente più facile usare i loro separati Spray-JSON libreria, dal momento che si collega direttamente al loro meccanismo di classificazione, ma penso che potresti riuscire a sposare qualsiasi cosa desideri. Spray-JSON è in grado di gestire una classe di casi con una linea di colla.

A proposito, la linea val bar: Bar = ??? nel codice di esempio verrà eseguito al momento della definizione del percorso, non quando una richiesta arriva, come presumibilmente si desidera. Si prega di leggere il Capire la struttura DSL sezione dei documenti spray-routing.


2 per risposta № 3

Capito grazie, ecco la mia risposta

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)
}
}
}
}
}

La mia richiesta http con l'app curl

curl -i "http://localhost:8080/api/userservice" -X POST -H "Content-Type: application/json" -d "{"username": "admin", "password": "pwd"}"