From a74278297fd7e4a520815748512b58222f9cf197 Mon Sep 17 00:00:00 2001 From: tkqubo Date: Thu, 4 Feb 2016 00:30:13 +0900 Subject: [PATCH] feat: cors --- .../qubo/seed/routes/ApiRouterActor.scala | 5 +- .../github/qubo/seed/routes/CorsHelper.scala | 55 +++++++++++++++++++ 2 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 src/main/scala/com/github/qubo/seed/routes/CorsHelper.scala diff --git a/src/main/scala/com/github/qubo/seed/routes/ApiRouterActor.scala b/src/main/scala/com/github/qubo/seed/routes/ApiRouterActor.scala index b2c913c..2176531 100644 --- a/src/main/scala/com/github/qubo/seed/routes/ApiRouterActor.scala +++ b/src/main/scala/com/github/qubo/seed/routes/ApiRouterActor.scala @@ -5,7 +5,7 @@ import java.util.NoSuchElementException import akka.actor.{Actor, ActorContext, ActorLogging} import com.github.qubo.seed.swagger.{SwaggerDefinition, SwaggerDefinitionConfig} import spray.http.MediaTypes._ -import spray.http.StatusCodes +import spray.http.{AllOrigins, StatusCodes} import spray.httpx.SprayJsonSupport import spray.json.DefaultJsonProtocol import spray.routing.{ExceptionHandler, HttpService, Route} @@ -19,6 +19,7 @@ class ApiRouterActor with DefaultJsonProtocol with SprayJsonSupport with UserApi + with CorsHelper with HttpService { def actorRefFactory: ActorContext = context implicit val ec: ExecutionContext = actorRefFactory.dispatcher @@ -28,7 +29,7 @@ class ApiRouterActor ) def receive: Receive = runRoute( - respondWithMediaType(`application/json`) { + (cors(AllOrigins) & respondWithMediaType(`application/json`)) { apiRoute ~ swaggerRoute } ~ swaggerUiRoute ) diff --git a/src/main/scala/com/github/qubo/seed/routes/CorsHelper.scala b/src/main/scala/com/github/qubo/seed/routes/CorsHelper.scala new file mode 100644 index 0000000..a8f654b --- /dev/null +++ b/src/main/scala/com/github/qubo/seed/routes/CorsHelper.scala @@ -0,0 +1,55 @@ +package com.github.qubo.seed.routes + + +import spray.http.HttpHeaders._ +import spray.http.HttpMethods._ +import spray.http._ +import spray.routing._ +import spray.util._ + +trait CorsHelper { + this: HttpService => + val MaxAge = 1728000 + + def p3p(value: String = """CP="CAO PSA OUR""""): Directive0 = + mapHttpResponseHeaders(_ ++ List(RawHeader("P3P", value))) + + def cors(origins: Seq[String]): Directive0 = + cors(SomeOrigins(origins.map(HttpOrigin(_)))) + + def cors(origin: String): Directive0 = + if (origin == "*") { + cors(AllOrigins) + } else { + cors(SomeOrigins(Seq(HttpOrigin(origin)))) + } + + def cors(origins: AllowedOrigins): Directive0 = mapRequestContext { ctx => + val originHeader = if (origins == AllOrigins) { + ctx.request.headers.findByType[`Origin`] + } else { + None + } + val resOrigins = originHeader.map { case Origin(origin) => SomeOrigins(origin) }.getOrElse(origins) + ctx.withRouteResponseHandling({ + //It is an option request for a resource that responds to some other method + case Rejected(x) if ctx.request.method.equals(HttpMethods.OPTIONS) => + ctx.complete(HttpResponse().withHeaders( + allowOriginHeader(resOrigins) ++ fixedCorsHeaders + )) + }).withHttpResponseHeadersMapped { headers => + allowOriginHeader(resOrigins) ++ fixedCorsHeaders ++ headers + } + } & p3p() + + private def fixedCorsHeaders: List[HttpHeader] = + List( + `Access-Control-Allow-Methods`(GET, POST, PATCH, PUT, DELETE, OPTIONS), + `Access-Control-Allow-Headers`("Content-Type,Accept-Language,Authorization"), + `Access-Control-Allow-Credentials`(true), + `Access-Control-Max-Age`(MaxAge) + ) + + private def allowOriginHeader(origins: AllowedOrigins): List[HttpHeader] = + List(`Access-Control-Allow-Origin`(origins)) +}