CORS support for Spray applications
This tutorial shows how we can add CORS support for API with Spray
- Synopsis
- Assumptions
- Requisites
- Motivation
- Installation
- Implementing CORS directive
- Tests - No CORS support
- Tests - CORS support
- Code
Synopsis
This project explains how to implement a REST API with Spray that has CORS support
Assumptions
I assume you have experience and your working environment ready to work with the below technologies/frameworks:
Akka, SBT, CORS, Spray, cURL, Scala, Git
Requisites
This post is oriented to these people who have experience working with Scala and are starting to do it with Spray for REST API implementations.
Motivation
Learning to declare your own Spray directives
Developing Rest API’s that have CORS support
Installation
The repository contains a couple of branches:
no-cors: Obviously this branch does not have CORS support
cors: This one contains CORS support.
Implementing CORS directive
The below pieces of code show us how to implements a new directive and have use of it in our rest API. The below code can be found in com.wesovi.demo.api.Api.scala
Declaring a new directive:
trait CORSSupport extends Directives { private val CORSHeaders = List( `Access-Control-Allow-Methods`(GET, POST, PUT, DELETE, OPTIONS), `Access-Control-Allow-Headers`("Origin, X-Requested-With, Content-Type, Accept, Accept-Encoding, Accept-Language, Host, Referer, User-Agent"), `Access-Control-Allow-Credentials`(true) ) def respondWithCORS(origin: String)(routes: => Route) = { val originHeader = `Access-Control-Allow-Origin`(SomeOrigins(Seq(HttpOrigin(origin)))) respondWithHeaders(originHeader :: CORSHeaders) { routes ~ options { complete(StatusCodes.OK) } } } }
Making use of the new directive
trait Api extends Directives with RouteConcatenation with CORSSupport with ConfigHolder{ this: CoreActors with Core => val routes = respondWithCORS(config.getString("origin.domain")) { pathPrefix("api") { new DemoRoute().route } } val rootService = system.actorOf(ApiService.props(config.getString("hostname"), config.getInt("port"), routes)) }
Tests - No CORS support
checkout branch
checkout no-cors
Run spray-can server:
sbt compile run
Run curl command : We will simulate a CORS request with the below command,
curl \ --verbose \ --header 'Origin: http://localhost:9292' \ --request OPTIONS \ --header 'Access-Control-Request-Headers: Origin, Accept, Content-Type' \ --header 'Access-Control-Request-Method: GET' \ --header 'Access-Control-Max-Age: 0' \ --header 'Set-Cookie: test=test' \ http://localhost:8878/api/demo/status
The server response
* Hostname was NOT found in DNS cache * Trying 127.0.0.1... * Connected to localhost (127.0.0.1) port 8878 (#0) > OPTIONS /api/demo/status HTTP/1.1 > User-Agent: curl/7.37.1 > Host: localhost:8878 > Accept: */* > Origin: http://localhost:9292 > Access-Control-Request-Headers: Origin, Accept, Content-Type > Access-Control-Request-Method: GET > Access-Control-Max-Age: 0 > Set-Cookie: test=test > < HTTP/1.1 405 Method Not Allowed * Server spray-can/1.3.3 is not blacklisted < Server: spray-can/1.3.3 < Date: Sat, 30 May 2015 07:01:44 GMT < Allow: GET < Content-Type: text/plain; charset=UTF-8 < Content-Length: 47
Tests - CORS support
checkout branch
checkout cors
Run spray-can server:
sbt compile run
Run curl command : We will simulate a CORS request with the below command,
curl \ --verbose \ --header 'Origin: http://localhost:9292' \ --request OPTIONS \ --header 'Access-Control-Request-Headers: Origin, Accept, Content-Type' \ --header 'Access-Control-Request-Method: GET' \ --header 'Access-Control-Max-Age: 0' \ --header 'Set-Cookie: test=test' \ http://localhost:8878/api/demo/status
The server response
* Hostname was NOT found in DNS cache * Trying 127.0.0.1... * Connected to localhost (127.0.0.1) port 8878 (#0) > OPTIONS /api/demo/status HTTP/1.1 > User-Agent: curl/7.37.1 > Host: localhost:8878 > Accept: */* > Origin: http://localhost:9292 > Access-Control-Request-Headers: Origin, Accept, Content-Type > Access-Control-Request-Method: GET > Access-Control-Max-Age: 0 > Set-Cookie: test=test > < HTTP/1.1 200 OK * Server spray-can/1.3.3 is not blacklisted < Server: spray-can/1.3.3 < Date: Sat, 30 May 2015 06:43:01 GMT < Access-Control-Allow-Origin: http://localhost < Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS < Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept, Accept-Encoding, Accept-Language, Host, Referer, User-Agent < Access-Control-Allow-Credentials: true < Content-Type: text/plain; charset=UTF-8 < Content-Length: 2
Code
* [Source code] (https://github.com/wesovi/spray-cors-demo)