使用Spray Routing,我希望有一个指令将查询字符串参数与JSON实体合并,两者都是可选的。我想在任何编组发生之前发生这种情况。
这样的事情:
val myRoute = mergedParametersAndEntity(as[DomainSpecificClass]) { myobj =>
// ... code code code ...
complete(OK, myobj.someMethod)
}
基本上我希望的是以下行为:
当有人提出以下请求时:
POST /v1/resource?a=helloQS&b=helloQS
Content-Type: application/json
{"a":"helloFromJson","c":"helloFromJson"}
然后上面的对象(myobj
)可以包含键:
a -> helloFromJson
b -> helloQS
c -> helloFromJson
换句话说,请求正文中指定的项将覆盖查询字符串中的内容。我知道这一定是可能的,但我根本无法弄清楚如何做到这一点。有人可以帮忙吗?
谢谢!
答案 0 :(得分:2)
我的建议:不要采取这种方法。感觉很脏。如果可以,请回到绘图板。
考虑到这一点,您将无法使用Spray中现有的JSON编组支持插入您想要的合并。你需要自己缝合在一起。这样的事情至少应该指向正确的方向(如果它是你必须走的方向):
import org.json4s.JsonAST._
import org.json4s.JsonDSL._
def mergedParametersAndEntity[T](implicit m:Manifest[T]): Directive1[T] = {
entity(as[JObject]).flatMap { jObject =>
parameterMap flatMap { params =>
val left = JObject(params.mapValues { v => JString(v) }.toSeq : _*)
provide(left.merge(jObject).extract[T])
}
}
}
答案 1 :(得分:0)
如果还有人想知道如何在这里做这个,那么一个小的Spray指令允许在解组之前将param值复制到JSON。它使用JSON镜头(https://github.com/jrudolph/json-lenses)在解组之前修改请求体。
def updateJson(update: Update): Directive0 = {
mapRequest((request: HttpRequest) => {
request.entity match {
case Empty => request
case NonEmpty(contentType, data) =>
try {
request.copy(entity = HttpEntity(`application/json`, JsonParser(data.asString).update(update).toString))
}
catch {
case e: ParsingException => request
}
}
})
}
以下是你如何使用它。假设您要使用PUT请求更新资源,并将URL从URL传递给unmarshaller(这是一个非常常见的场景btw):
// Model
case class Thing(id: Long, title: String, description: String, createdAt: DateTime)
// Actor message for the update operation
case class UpdateThing(id: Long, title: String)
// Spray routing
def thingsRoute =
path("things" / Segment) { id =>
put {
updateJson(field("id") ! set[Long](id)) {
entity(as[UpdateThing]) { updateThing =>
complete {
// updateThing now has the ID set
(someActor ? updateThing).mapTo[Thing]
}
}
}
}
}
您还可以将其与parameters
指令结合使用,以设置任意GET参数。