add explicit rejection for missing header [1.1.1]

22 views
Skip to first unread message

Ryan O'Rourke

unread,
Apr 25, 2016, 3:29:14 PM4/25/16
to spray.io User List
headerValue and related directives reject with an empty rejection list when the header can't be found. I'd like to add an explicit MissingHeaderRejection if the header isn't there.

(In my example I'm using the clientIP directive, which is a headerValuePF checking three related headers, but it's the same idea.)

I came up with this:

     val clientIPAddr = mapRejections(r => if (r.isEmpty) List(MissingHeaderRejection(`X-Forwarded-For`.name)) else r) & clientIP.map {
        _.toOption.map(_.getHostAddress).getOrElse("unknown")
    }

This works, but is not perfect. If I understand it correctly, the "mapRejections" directive will apply to the entire inner route, so even if the clientIP matches, if something nested down below it in the routing tree also rejects with an empty list, then this will spuriously add the MissingHeaderRejection.

e.g:

    clientIPAddr { ip =>
        reject // MissingHeaderRejection(`X-Forwarded-For`.name) will be added if we get here. do not want.
    }

It seems like this should be pretty trivial to do, but I can't figure it out. None of the following will work (they don't compile):

    val clientIPAddr = clientIP.map {
        _.toOption.map(_.getHostAddress).getOrElse("unknown")
    }.recover { _ => reject(MissingHeaderRejection(`X-Forwarded-For`.name))}

    val clientIPAddr = clientIP.map {
        _.toOption.map(_.getHostAddress).getOrElse("unknown")
    } | reject(MissingHeaderRejection(`X-Forwarded-For`.name))

Ryan O'Rourke

unread,
Apr 26, 2016, 6:55:42 PM4/26/16
to spray.io User List
Ah, I figured it out. The secret is flatMap!

    val clientIPAddr = clientIP.flatMap { 
            _.toOption match {
              case Some(addr) => provide(addr.getHostAddress)
              case None       => reject(MissingHeaderRejection(`XForwarded-For`.name))     
    }

Like it says in the documentation (duh), you need to use flatMap to change the "extracting" nature of the directive. Just what is needed here.
Reply all
Reply to author
Forward
0 new messages