scala - Spray enrutamiento 404 respuesta
akka (2)
Spray ya tiene soporte para su caso de uso: un valor de opción None
se EmptyEntity
de forma predeterminada en una EmptyEntity
. Esto es probablemente lo que estaba viendo antes de realizar cualquier cambio: un 200 con un documento vacío. Hay una directiva que convierte un documento vacío en un 404, rejectEmptyResponse
, que envuelve las partes de su ruta donde desea este comportamiento.
Su ruta se vería así:
rejectEmptyResponse {
path("products" / PathElement) { productID:String =>
val productFuture = // same as before
complete(productFuture)
}
}
Por supuesto, puede colocar el rejectEmptyResponse
dentro de la ruta dependiendo de si desea envolver más partes de ruta con él.
Más información:
Tengo un servicio que devuelve una Opción [ProductDoc] en un futuro (como un akka pregunta)
¿Cómo respondo en enrutamiento de rociado para que un producto válido responda con un producto pero uno desconocido pero bien formado devuelva un 404?
Quiero que el código llene el espacio aquí:
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
}
}
La única forma en que puedo ponerme a trabajar es con esta abominación:
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
}
}
}
}
Esta no puede ser la forma correcta de lograr esto, ¿no? ¡Esto parece un patrón bastante simple que ya debe haber sido resuelto por alguien!
Tengo el mismo problema hace unos días y llegué a esta solución:
Agregué este método a mi actor.
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
}
Por supuesto, puedes elegir una excepción que te guste, NotFoundException es una de las mías ...
Llame a este en su resultado para responder a la pregunta de su actor (este es un ejemplo para usar ReactiveMongo, reemplace collection.find(query).headOption
con su Future[Option]
):
failIfEmpty(collection.find(query).headOption, id)
Luego, agregue un ExceptionHandler a su servicio (donde se define su ruta) como lo siguiente:
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]
}
De esta manera puede manejar varios errores diferentes que pueden ocurrir en sus resultados futuros.