Produce an error with scala and spray

155 Views Asked by At

I try to create simple CRUD application with scala and spray-routing. I have the following route:

override def receive = runRoute {
    path("entities" / LongNumber) { id =>
      get {
        produce(instanceOf[MyEntity]) {
          func => ctx => heavyLookupById(id, func)
        }
    }
}

I wrote this according to the official documentation http://spray.io/documentation/1.2.2/spray-routing/marshalling-directives/produce/

MyEntity is the following (doesn't matter really):

case class MyEntity(text: String, id: Option[Long])

And I have the following json-support object

object MyJsonSupport extends DefaultJsonProtocol with SprayJsonSupport {
    implicit val format = jsonFormat2(MyEntity)
}

The "heavyLookupById" function contains some heavy blocking computations (suppose database queries or http requests) and I have to deal with scala future because of it:

def heavyLookupById(id: Long, func: MyEntity => Unit) = {
   // ... heavy computations
   future onSuccess { case x => func(x) }
}

But what should I do if my future fails? I want to respond with something like bad request (400) or not found (404) HTTP errors, but how to do it? If I don't invoke "func" inside the "heavyLookupById" - request just hanging - I believe it will fail by default server timeout (1 minute or so).

1

There are 1 best solutions below

1
mericano1 On BEST ANSWER

You have the RequestContext (ctx) so you can call reject, failWith or any other methods available on the RequestContext.

  val route = path("entities" / LongNumber) { id =>
    get {
      produce(instanceOf[MyEntity]) {
        func => ctx => heavyLookupById(id, func, ctx)
      }
    }
  }

  def heavyLookupById(id: Long, func: MyEntity => Unit, ctx: RequestContext) = {
    // ... heavy computations
    val future = Future.successful(MyEntity("Hello", Some(1)))
    future.onComplete {
      case Success(value) => func(value)
      case Failure(ex) => ctx.failWith(ex)
    }

  }

Personally prefer handleWith rather than produce, I find it a bit easier to read.

Also in case of failure spray will just return a 500 that you can customize with exceptionHandlers.

  def heavyLookupById(id: Long) = {
    // ... heavy computations
    Future.successful(MyEntity("Hello", Some(1)))
  }


  val route = path("entities" / LongNumber) { id =>
    get {
      handleWith(heavyLookupById)
    }
  }