Spray routing 404 response
Asked Answered
G

2

16

I have a service which returns an Option[ProductDoc] in a Future (as an akka ask)

How do I respond in spray routing so that a valid product repsonds with a product but an unknown but well formed one returns a 404?

I want the code to fill in the gap here :

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

    }
}

The only way I can get to work is with this abomination :

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
        }
      }
    }
  }

This can't be the correct way to achieve this, surely? This seems like a pretty simple pattern that must have been solved by someone already!

Galactic answered 11/3, 2013 at 19:37 Comment(0)
H
17

Spray already has support for your use case: An option value None is marshalled to an EmptyEntity by default. This is probably what you were seeing before you made any changes: a 200 with an empty document. There's a directive which converts an empty document into a 404, rejectEmptyResponse, which you wrap around parts of your route where you want this behavior.

Your route would then look just like this:

  rejectEmptyResponse {
    path("products" / PathElement) { productID:String =>
      val productFuture = // same as before
      complete(productFuture)
    }
  }

Of course, you can put the rejectEmptyResponse inside the path depending on whether you want to wrap more route parts with it.

More info:

Hild answered 12/3, 2013 at 9:23 Comment(4)
Does this mean that my promise has to return success but with empty response when I have an error in my logic or dao layer?Kuykendall
I don't understand completely what you want to achieve. This topic was about Future[Option[T]]. Using the structure above you can return Success(Some(x)) for a regular response, Success(None) for 404, and Failure(x) to generate an error response. Is that what you are asking? If you have a more specific question, please ask at the spray-user mailing list.Hild
It doesn't sound to me Spray is really supporting this use case. What about a 200 empty response from e.g. a delete command? Will it become 404?Bite
What exactly do you mean by "this use case"? What result you get depends on whether you use rejectEmptyResponse or not. You can decide by using the directive where you need it.Hild
I
0

I has the same problem a few days ago and came to this solution:

I added this method to my 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
}

Of course you can choose an Exception you like, NotFoundException is one of my own...

Call this on your result to answer to the ask from your actor (this is an example for using ReactiveMongo, replace collection.find(query).headOption with your Future[Option]):

failIfEmpty(collection.find(query).headOption, id)

Then add an ExceptionHandler to your service (where your route is defined) like the following:

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]
}

This way you can handle several different errors that my occur in your future-results.

Itagaki answered 12/3, 2013 at 8:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.