code-small.jpg

 

Cake Team Blogs

Cake Solutions architects, implements and maintains modern and scalable software, which includes server-side, rich browser applications and mobile development. Alongside the software engineering and delivery, Cake Solutions provides mentoring and training services. Whatever scale of system you ask us to develop, we will deliver the entire solution, not just lines of code. We appreciate the importance of good testing, Continuous Integration and delivery, and DevOps. We motivate, mentor and guide entire teams through modern software engineering. This enables us to deliver not just software, but to transform the way organisations think about and execute software delivery.

 

 

Control-Allow-Origin in Spray

Posted by Jan Machacek

Find me on:

21/03/13 08:46

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

Topics: Spray, AJAX

Posts by Topic

see all

Subscribe to Email Updates