Напишите простой сервер JSON REST с помощью spray в scala

Я хочу реализовать простой сервер JSON REST с помощью spray в 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

мой основной стартовый код следующим образом:

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

две наиболее важные открытые проблемы с этим кодом, о которых я знаю:

  1. Я в зависимости от Джексона, но от поиска в интернете кажется, что spray должен иметь какую-то встроенную поддержку для сериализации и десериализации простых объектов case или списков объекты дела.

  2. Я не уверен, что "лучший", самый идиоматичный и краткий способ получить контент из запроса post и Маршалл его в json, чтобы я мог выполнить некоторые вычисления на объекте (объектах) класса case

кто-нибудь знает лучший подход? Есть ли способ сделать маршалинг автоматическим, чтобы я мог выполнить что-то вроде complete { caseObject } и caseObject автоматически конвертировать в json (и наоборот с определением POST метод)?

3 ответов


определенно используйте спрей json. Обычно вы разделяете модели данных в свой собственный файл:

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

тогда в маршруте

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

Я не могу понять, почему этот вопрос был проголосован-он кажется конкретным и хорошо выраженным.

Это немного трудно найти, но спрей документов обложка случай извлечения класса под трассой брызг / предварительными темами. Было бы бессмысленно повторять свои объяснения, но в основном вы хотите использовать as[Foo] десериализовать содержимое HTTP в объекты. The может использоваться для этого для тела запроса, как показано в этом больше примеров распыление DSL. mapTo (используется в том же примере), вероятно, что вы хотите сериализовать объект в ответ.

для JSON, вероятно, было бы проще использовать их отдельные спрей-JSON-формате библиотека, так как она подключается прямо к их механизму typeclass, но я думаю, что вы могли бы с некоторыми усилиями жениться на чем угодно. Спрей-JSON может обрабатывать класс корпуса с одной линией клея.

кстати, строку val bar: Bar = ??? в вашем примере кода будет выполняться во время определения маршрута, а не при поступлении запроса, как вы предположительно хотите. Ознакомьтесь, пожалуйста понимание структуры DSL раздел документов маршрутизации распыления.


понял спасибо, вот мой ответ

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

мой http-запрос с curl app

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