我有一个服务,它在Future中返回一个Option [ProductDoc](作为akka问题)
我如何回应喷涂路线,以便有效产品代表产品但是未知但形状良好的产品会返回404?
我希望代码填补这里的空白:
get {
path("products" / PathElement) { productID:String =>
val productFuture = (productService ? ProductService.Get(productID)).mapTo[Option[ProductDoc]]
// THE CODE THAT GOES HERE SO THAT
// IF PRODUCT.ISDEFINED RETURN PRODUCT ELSE REJECT
}
}
我可以上班的唯一方法就是这种可憎的行为:
get {
path(PathElement) { productID:String =>
val productFuture = (productService ? ProductService.Get(productID)).mapTo[Option[ProductDoc]]
provide(productFuture).unwrapFuture.hflatMap {
case x => provide(x)
} { hResponse:shapeless.::[Option[ProductDoc], HNil] =>
hResponse.head match {
case Some(product) => complete(product)
case None => reject
}
}
}
}
这肯定不是实现这一目标的正确方法吗?这似乎是一个非常简单的模式,必须已经被某人解决了!
答案 0 :(得分:17)
Spray已经支持您的用例:默认情况下,选项值None
被编组为EmptyEntity
。这可能是您在进行任何更改之前看到的内容:带有空文档的200。有一个指令可以将一个空文档转换为404 rejectEmptyResponse
,您可以在其中包含您希望此行为的部分路径。
您的路线看起来就像这样:
rejectEmptyResponse {
path("products" / PathElement) { productID:String =>
val productFuture = // same as before
complete(productFuture)
}
}
当然,您可以将rejectEmptyResponse
放在路径中,具体取决于您是否要用它包装更多的路径部分。
更多信息:
答案 1 :(得分:0)
我几天前遇到同样的问题,并找到了解决方案:
我将此方法添加到我的演员
def failIfEmpty[T](item: Future[Option[T]], id: String) = {
(item map {
case Some(t) => t
case None => throw NotFoundException(Message(s"id '$id' could not be found",`ERROR`))
}) pipeTo sender
}
当然你可以选择你喜欢的Exception,NotFoundException是我自己的......
在结果上调用此方法来回答您演员的提问(这是使用ReactiveMongo的示例,将collection.find(query).headOption
替换为Future[Option]
):
failIfEmpty(collection.find(query).headOption, id)
然后将ExceptionHandler添加到您的服务(定义路由的位置),如下所示:
implicit val klaraExceptionHandler = ExceptionHandler.fromPF {
case InternalServerErrorException(messages) => complete(InternalServerError, messages)
case NotFoundException(message) => complete(NotFound, message)
case ValidationException(messages) => complete(PreconditionFailed, messages)
[and so on]
}
通过这种方式,您可以处理我在未来结果中出现的几种不同错误。