How to set status code and headers in spray-routing based on a future result

468 Views Asked by At

I'm using spray-routing with Akka to define a route like

def items = path("items") {
  get {
    complete {
      actor.ask(GetItems)(requestTimeout).mapTo[Either[NoChange, Items]] map {
         result => result match {
            case Left(_) => StatusCodes.NotModified
            case Right(items) => 
               // here I want to set an HTTP Response header based on a 
               // field within items -- items.revision
               items
         }
      }
    }
  }
}

The actor.ask returns a Future that gets mapped to a Future[Either[NoChange, Items]]. "complete" is happy to deal with the Future[StatusCodes...] or the Future[Items] but I'm not sure how to set an HTTP Response header within the Future.

If the header weren't being set within the Future then I could just wrap the complete in a directive but how do I set a header within the complete?

I'm using Spray 1.2.0.

Thanks for any pointers in the right direction!

1

There are 1 best solutions below

1
On BEST ANSWER

If you are trying to do this inside of complete all branches of the expression inside must result in a type that can be marshalled by complete.

You could try a structure like this to make it work:

complete {
  actor.ask(GetItems)(requestTimeout).mapTo[Either[NoChange, Items]] map {
     result => result match {
        case Left(_) => StatusCodes.NotModified: ToResponseMarshallable
        case Right(items) => 
           // here I want to set an HTTP Response header based on a 
           // field within items -- items.revision

           val headers = // items...
           HttpResponse(..., headers = headers): ToResponseMarshallable
     }
  }
}

This ensures that the type of the expression you pass to complete is Future[ToResponseMarshallable] which should always be marshallable.

A better way, though, is to use the onSuccess directive that lets you use other directives after a future was completed:

get {
  def getResult() = actor.ask(GetItems)(requestTimeout).mapTo[Either[NoChange, Items]]
  onSuccess(getResult()) {
    case Left(_) => complete(StatusCodes.NotModified)
    case Right(items) => 
      // do whatever you want, e.g.
      val extraHeaders = // items.revisions
      respondWithHeaders(extraHeaders) {
        complete(...)
      }
  }
}