当我使用Spray.io开发RESTful API时,我应该如何构建我的应用程序?
我已经看到了关于如何拆分Spray应用程序的这个答案,但我对此并不满意,因为它似乎没有使用"每个请求的一个actor"方法.我可以根据路径将根执行者的请求转发给我的应用程序中的其他actor,并在这些actor中定义相关的路由吗?
谢谢
我想支持提交到同一网址的几种不同的内容类型:
例如:
application/x-www-form-urlencoded,multipart/form-data,application/json
我想做的事情如下:
post {
contentType(`application/x-www-form-urlencoded`) |
contentType(`multipart/form-data`) {
// user POSTed a form
entity(as[MyCaseClass]) { data =>
complete { data.result }
}
} ~ contentType(`application/json`) {
// user POSTed a JSON object
entity(as[MyCaseClass]) { data =>
complete { data.result }
}
}
}
Run Code Online (Sandbox Code Playgroud)
我认为可能有一些方法可以使用自定义编组和解组,但我只需要在我的服务中的一两个位置,这看起来很简单.有人可以帮忙吗?
我有这条路线:
val routes =
pathPrefix("api") {
path("ElevationService" / DoubleNumber / DoubleNumber) { (long, lat) =>
post {
requestContext =>
println(long, lat)
}
}
}
Run Code Online (Sandbox Code Playgroud)
这很好用,我可以称之为ElevationService:
http://localhost:8080/api/ElevationService/39/80
Run Code Online (Sandbox Code Playgroud)
问题是,我还想在请求中解析发送给我的身体为JSON.它看起来如下:
{
"first": "test",
"second": 0.50
}
Run Code Online (Sandbox Code Playgroud)
我已经设法让它在一个单独的路径中工作,遵循实体指令的文档:
path("test") {
import scrive.actors.ScriveJsonProtocol
import spray.httpx.SprayJsonSupport._
post {
entity(as[ScriveRequest]) { scrive =>
complete(scrive)
}
}
}
Run Code Online (Sandbox Code Playgroud)
但我不知道如何将这两条路线合并为一条路线.因为它们包含在函数中,所以我不能在函数内调用参数long,我认为它们不存在于该范围内.相同或相反.latentity
我希望能够访问我的params和我的POST主体,然后调用传递所有数据的服务:
val elevationService = actorRefFactory.actorOf(Props(new ElevationService(requestContext)))
elevationService ! ElevationService.Process(long, lat, bodyParams)
Run Code Online (Sandbox Code Playgroud) 我不想明确写:
options { ... }
Run Code Online (Sandbox Code Playgroud)
对于我的喷涂路线中的每个入口点/路径.我想写一些通用代码来增加OPTIONS对所有路径的支持.它应该查看路由并从中提取支持的方法.
我无法粘贴任何代码,因为我不知道如何在Spray中处理它.
我这样做的原因是我想提供一个符合HATEOAS原则的自我发现的API.
我是新来的喷雾剂.我正在玩构建路由,虽然我设法使用参数指令从查询字符串中获取参数,但是当我希望其中一个参数成为列表时,我遇到了麻烦.
对于这个例子,我已经定义了这个案例类:
case class Person(name: String, friends: Int)
Run Code Online (Sandbox Code Playgroud)
我的路线目前看起来像这样:
path("test") {
get { parameters('name, 'friend ).as(Person) { p => complete(p) } }
}
Run Code Online (Sandbox Code Playgroud)
这工作正常,我可以做一个get:localhost:8080/test?name = jo&friends = 12并得到我的期望.
我想传递朋友ID的列表,而不仅仅是朋友的数量,所以我开始更改我的案例类,如下所示:
case class Person(name: String, friends: Array[Int])
Run Code Online (Sandbox Code Playgroud)
我的电话:localhost:8080/test?name = jo&friends = 1,2
这不编译.我得到一个类型不匹配:发现:Person.type required:spray.routing.HListDeserializer [shapeless.:: [String,shapeless.:: [String,shapeless.HNil]],?] get {parameters('name,'friend) ).as(Person){p => ^ comment:这指向P in .as(Person)
关于我做错了什么的任何想法?我喜欢这个怎么做的答案.更好的是解释它正在寻找的这种无形类型.谢谢
I tried a simple spray example app and i cannot access the route, I uploaded the example source code which does not work to github: spray-tomcat-example:
git clone https://github.com/avidanyum/spray-tomcat-example
mvn package
cp cp target/spray-tomcat-example-0.1-SNAPSHOT.war ~/tmp/tomcat/apache-tomcat-7.0.61/webapps/spraytomcat.war
cd ~/tmp/tomcat/apache-tomcat-7.0.61/bin
./catalina.sh jpda run
http://localhost:8080/spraytomcat/
Run Code Online (Sandbox Code Playgroud)
I get
"The requested resource could not be found."
Run Code Online (Sandbox Code Playgroud)
I defined the route as following:
class ServiceActor extends Actor with Service {
def actorRefFactory = context
def receive = runRoute(myRoute)
}
trait Service extends HttpService {
import com.example.domain.Person …Run Code Online (Sandbox Code Playgroud) 我有(以前)REST spray.io webservice.现在,我需要在我的一个方法中生成SESSIONID以与其他一些方法一起使用.我希望它在响应标题中.
基本上,我想象逻辑如下:
path("/...") {
get {
complete {
// some logic here
// .....
someResult match {
case Some(something) =>
val sessionID = generateSessionID
session(sessionID) = attachSomeData(something)
// here I need help how to do my imaginary respond with header
[ respond-with-header ? ]("X-My-SessionId", sessionID) {
someDataMarshalledToJSON(something)
}
case None => throw .... // wrapped using error handler
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
但是,它并不完整,我指的是respondWithHeader指令.我需要一个建议.
在这里回答我自己的问题,因为这花了我一天多的时间来弄清楚,这是一个非常简单的问题,我认为其他人可能会遇到.
在使用REST创建RESTful-esk服务时,我希望将具有字母数字id的路由作为路径的一部分进行匹配.这是我最初开始的:
case class APIPagination(val page: Option[Int], val perPage: Option[Int])
get {
pathPrefix("v0" / "things") {
pathEndOrSingleSlash {
parameters('page ? 0, 'perPage ? 10).as(APIPagination) { pagination =>
respondWithMediaType(`application/json`) {
complete("things")
}
}
} ~
path(Segment) { thingStringId =>
pathEnd {
complete(thingStringId)
} ~
pathSuffix("subthings") {
pathEndOrSingleSlash {
complete("subthings")
}
} ~
pathSuffix("othersubthings") {
pathEndOrSingleSlash {
complete("othersubthings")
}
}
}
}
} ~ //more routes...
Run Code Online (Sandbox Code Playgroud)
这没有问题编译,但是当使用scalatest来验证路由结构是否正确时,我很惊讶地发现这种类型的输出:
"ThingServiceTests:"
"Thing Service Routes should not reject:"
- should /v0/things
- should /v0/things/thingId
- should …Run Code Online (Sandbox Code Playgroud) 此代码导致编译错误could not find implicit value for parameter marshaller:
spray.httpx.marshalling.ToResponseMarshaller[List[akka.actor.ActorRef]].
我不认为问题是ActorRef,因为改变它.mapTo[List[String]]显示相同的编译错误
一般来说,喷雾如何编组与所有暗示有点混淆 - 有没有办法使这个明确,例如ListProtocol.marshal(value)?
import akka.actor.Actor
import spray.http.HttpResponse
import spray.http.HttpRequest
import spray.http.Uri
import spray.http._
import spray.routing._
import HttpMethods._
import akka.actor.ActorRef
import akka.pattern.ask
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.Success
import scala.util.Failure
import spray.http.StatusCodes.InternalServerError
import spray.json.DefaultJsonProtocol
import spray.httpx.SprayJsonSupport._
import spray.httpx.marshalling._
import spray.http._
class HttpApi(val manager: ActorRef) extends HttpServiceActor {
def receive = runRoute {
path("nodes") {
get {
onComplete(manager.ask(NodeList())(3.seconds).mapTo[List[ActorRef]]) {
case Success(value) => {
// …Run Code Online (Sandbox Code Playgroud)