使用spala中的spray编写一个简单的json REST服务器

时间:2014-06-06 23:29:12

标签: json scala spray

我想使用支持以下路径的spala实现一个简单的json REST服务器:

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. 我依赖于jackson,但是从网上搜索,似乎spin应该有一些内置支持来序列化和反序列化简单案例对象或案例对象列表。

  2. 我不确定"最佳",最常用和最简洁的方式从post请求中获取内容并将其编组到json中,以便我可以对案例执行一些计算class object(s)

  3. 有谁知道最好的方法?有没有办法让编组自动化,所以我可以执行像complete { caseObject }这样的事情并让caseObject自动转换为json(反之亦然,定义POST方法)?

3 个答案:

答案 0 :(得分:20)

绝对使用喷雾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)
        }
      }
    }
  }

答案 1 :(得分:7)

我无法想象为什么这个问题被低估了 - 它似乎具体而且表达得很好。

有点难以找到,但Spray文档涵盖了喷涂路由/高级主题下的case class extraction。在这里重复解释是没有意义的,但基本上你想使用as[Foo]将HTTP内容反序列化为对象。 entity指令可用于为请求正文执行此操作,如此longer example of the spray-routing DSL所示。 mapTo(在同一示例中使用)可能是您要为响应序列化对象的内容。

对于JSON,最简单的方法是使用它们独立的Spray-JSON库,因为它直接插入了它们的类型类机制,但我认为你可以通过一些努力结合你想要的任何东西。 Spray-JSON可以使用一行胶水来处理案例类。

顺便说一句,示例代码中的行val bar: Bar = ???将在定义路由时执行,而不是在请求进入时执行,正如您可能想要的那样。请阅读喷涂路线文档的Understanding the DSL Structure部分。

答案 2 :(得分:2)

谢谢,这是我的回答

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

使用curl app的我的http请求

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