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