在Spray 1.2.0路由中将查询字符串参数与JSON实体组合在一起

时间:2014-01-23 20:16:17

标签: json scala spray spray-json spray-dsl

使用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

换句话说,请求正文中指定的项将覆盖查询字符串中的内容。我知道这一定是可能的,但我根本无法弄清楚如何做到这一点。有人可以帮忙吗?

谢谢!

2 个答案:

答案 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参数。