Control-Allow-Origin in Spray

It is 2013, we have modern browsers. And these browsers do not like to load resources from origin that’s different from the resource being displayed. So, if you have a page at http://www.foo.com and want to make a request to http://api.bar.com, the browsers will (and should) reject it, unless you explicitly allow it.

We use the Access-Control-Allow-Origin HTTP header to do just that. Now, if you have a Spray route that uses the Spray JSON support to marshal the responses, you’re a bit stuck. You have something like

...
path("transactions" / JavaUUID) { id: TransactionId =>
  get {
    complete {
       (actor ? Messsage(id)).mapTo[Response]
    }
  }
}

Now what? You still want to use the convenient marshallers (e.g. Spray JSON), but you want to specify the HTTP header. I have created the CrossLocationRouteDirectives, which lets you do just that. The only downside is that there is another block inside complete.

... with CrossLocationRouteDirectives {

  val origin = "http://www.foo.com"

  path("transactions" / JavaUUID) { id: TransactionId =>
    get {
      complete {
        fromObjectCross(origin) {
          (actor ? Messsage(id)).mapTo[Response]
        }
      }
    } 
  }

This makes everything tick along quite nicely: you still have the marshalling infrastructure, and you can now allow the remote origin by setting the value of the Access-Control-Allow-Origin HTTP header. To skin the can it yet another way (a charming British saying!), you can now have some jQuery AJAX in your pages on http://www.foo.com that make requests to http://api.bar.com.

$.ajax({
    type: "GET",
    url: "http://api.bar.com/transactions/...",
}).done(function(data) {
   // ponies & unicorns
}).fail(function(jqXHR, textStatus, errorThrown) {
   // PHP!
});

The code of CrossLocationRouteDirectives is quite simply

trait CrossLocationRouteDirectives extends RouteDirectives {

  implicit def fromObjectCross[T: Marshaller](origin: String)(obj: T) =
    new CompletionMagnet {
      def route: StandardRoute = 
        new CompletionRoute(OK, 
              RawHeader("Access-Control-Allow-Origin", origin) :: Nil, obj)
    }

  private class CompletionRoute[T: Marshaller](status: StatusCode, 
                                               headers: List[HttpHeader], 
                                               obj: T)
    extends StandardRoute {

    def apply(ctx: RequestContext) {
      ctx.complete(status, headers, obj)
    }
  }
}
This entry was posted in Jan's Blog and tagged , , . Bookmark the permalink.

6 Responses to Control-Allow-Origin in Spray

  1. Pingback: This week in #Scala (22/03/2013) | Cake Solutions Team Blog

  2. Mathias says:

    Jan,
    interesting, thanks!
    One question: Wouldn’t the `respondWithHeader` directive simplify things a little bit?
    You could then have:

    val fromObjectCross(origin: String) =
    respondWithHeader(RawHeader(“Access-Control-Allow-Origin”, origin))

    and use it like this:

    get {
    fromObjectCross(origin) {
    complete {
    (actor ? Messsage(id)).mapTo[Response]
    }
    }
    }

    Cheers,
    Mathias

  3. Jan Machacek says:

    Hey Mathias,

    Yes, it would. Let me try it out and report back. (Probably after my holidays–yay!–next week.)

    Cheers,
    Jan

  4. Jason says:

    Hi Jan, have you got the source handy for the directive CrossLocationRouteDirectives? I’ve tried Mathias suggestion but it doesn’t seem to be working.

    Cheers, Jason.

  5. Jason says:

    Sorry, please disregard previous comment, I didn’t see the code at the bottom (rushing) will try it out. Thanks, Jason.

  6. Jan Machacek says:

    No worries. The code (with other bits & pieces) is at

    –J

Leave a Reply