Chaining rejection handlers

September 4, 2015

To serve some resources via Spray, it’s as easy as using the ‘getFromFile’ directive. If you want to fall back to an alternative because the file is not available, you can define a RejectionHandler to serve an alternative file.

In a web application I wanted to try some alternative names before falling back to a default image. The default solution requires you to nest all RejectionHandlers.

 def alternativesHandler = RejectionHandler {    
  case rejection =>    
   handleRejection(RejectionHandler {    
    case rejection => getFromFile("second-alternative")    
   }) {    
    getFromFile("first-alternative")    
   }    
 }    

This becomes quite messy very fast and is not very easy to read.
Chaining instead of nesting would improve it quite a bit.

 def alternativesHandler = chaining(RejectionHandler {    
  case rejection => getFromFile("first-alternative")    
 } >> RejectionHandler {    
  case rejection => getFromFile("second-alternative")    
 } >> RejectionHandler {    
  case rejection => getFromFile("third-alternative")    
 })

Here is the code which makes this possible. It only needs to be imported where the route is being defined. The ‘chaining’ method just tries each handler in the List. The implicit classes extends the classes with a ‘»’ method which allows the handlers to be chained in a list.

 trait RejectionHandlingChain {    
  type RejectionHandlerList = List[RejectionHandler]    

  implicit class RejectionHandlerListExt(handler: RejectionHandlerList) {    
   def >>(other: RejectionHandler): RejectionHandlerList = handler :+ other    
  }    

  implicit class RejectionHandlerExt(handler: RejectionHandler) {    
   def >>(other: RejectionHandler): RejectionHandlerList = List(handler, other)    
  }    

  import spray.routing.directives.RouteDirectives.reject    
  import spray.routing.directives.ExecutionDirectives.handleRejections    

  final def chaining(handlers: RejectionHandlerList): RejectionHandler = RejectionHandler {    
   case rejection => handlers match {    
    case Nil     =>    
     reject(rejection: _*)    
    case head :: tail =>    
     handleRejections(chaining(tail)) {    
      head(rejection)    
     }    
   }    
  }    
 }    

 object RejectionHandlingChain extends RejectionHandlingChain

Interesting cases & blogs

March 13, 2026

Long Overdue

Finally live The new site is live. I’ve been meaning to do this for a while — not because I […]

January 17, 2021

Netatmo integration in Home Assistant via MQTT

Instead of using the default Netatmo integration in Home Assistant, I switch to using an MQTT bridge. I found a […]

Scroll to Top