2014-06-06 6 views
21

Chcę zaimplementować prosty serwer json REST przy użyciu aerozolu w Scala, który obsługuje następujących trasach:Napisz prosty serwer json REST przy użyciu aerozolu w Scala

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 

Mój podstawowy kod startowy jest następujący:

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

dwóch najważniejszych otwartych kwestii z tym kodem, które znam są:

  1. jestem w zależności od jacksona, ale od przeszukiwania sieci, wygląda na to, że spray powinien mieć wbudowaną obsługę szeregowania i deserializacji prostych obiektów typu case lub list obiektów case.

  2. nie jestem pewien „najlepszy”, najbardziej idiomatyczne i zwięzły sposób, aby uzyskać zawartość z żądania POST i Marshall go do JSON tak, że mogę wykonać kilka obliczeń na obiekcie (s) Klasa przypadek

Czy ktoś zna najlepsze podejście? Czy istnieje sposób automatycznej synchronizacji, więc mogę wykonać komendę podobną do complete { caseObject } i automatycznie przekonwertować ją na json (i na odwrót, definiując metodę POST)?

Odpowiedz

20

Zdecydowanie użyj spray json. Zazwyczaj oddzielić modeli danych do własnego pliku:

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

Następnie w trasie

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

W języku 'spray-json' 1.3.3 impor można importować 'import spray.httpx.SprayJsonSupport._' – Core

+1

Gangstead - Dziękuję bardzo! bardzo pomocne!!! – Briksins

+0

Uważaj również na zamawianie/importowanie. –

7

Nie mogę sobie wyobrazić, dlaczego to pytanie zostało odrzucone - wydaje się konkretne i dobrze wyrażone.

To jest trochę trudne do znalezienia, ale okładka dokująca Spray docs case class extraction w temacie Spray Routing/Advanced Topics. Nie ma sensu powtarzać ich wyjaśnienia tutaj, ale zasadniczo chcesz użyć as[Foo] do deserializacji zawartości HTTP do obiektów. Do tego celu można zastosować dyrektywę entity, zgodnie z tym, co pokazano w tej publikacji: longer example of the spray-routing DSL. mapTo (używane w tym samym przykładzie) jest prawdopodobnie tym, co chcesz serializować obiekt dla odpowiedzi.

Dla JSON-a najprawdopodobniej najłatwiej byłoby korzystać z ich osobnej biblioteki Spray-JSON, ponieważ jest ona podłączona bezpośrednio do mechanizmu typeclass, ale myślę, że z pewnym wysiłkiem mógłbyś poślubić wszystko, co chcesz. Spray-JSON może obsłużyć klasę obudowy za pomocą jednej linii kleju.

Nawiasem mówiąc, linia val bar: Bar = ??? w kodzie przykładowym zostanie wykonana w czasie definiowania trasy, a nie w momencie otrzymania żądania, tak jak prawdopodobnie chcesz. Zapoznaj się z sekcją Understanding the DSL Structure dokumentowania trasowania natryskowego.

2

Got to dzięki, tutaj jest moja odpowiedź

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

Moje żądania HTTP z curl aplikacji

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