-
-
Save joseraya/176821d856b43b1cfe19 to your computer and use it in GitHub Desktop.
package com.agilogy.spray.cors | |
import spray.http.{HttpMethods, HttpMethod, HttpResponse, AllOrigins} | |
import spray.http.HttpHeaders._ | |
import spray.http.HttpMethods._ | |
import spray.routing._ | |
// see also https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS | |
trait CORSSupport { | |
this: HttpService => | |
private val allowOriginHeader = `Access-Control-Allow-Origin`(AllOrigins) | |
private val optionsCorsHeaders = List( | |
`Access-Control-Allow-Headers`("Origin, X-Requested-With, Content-Type, Accept, Accept-Encoding, Accept-Language, Host, Referer, User-Agent"), | |
`Access-Control-Max-Age`(1728000)) | |
def cors[T]: Directive0 = mapRequestContext { ctx => ctx.withRouteResponseHandling({ | |
//It is an option requeset for a resource that responds to some other method | |
case Rejected(x) if (ctx.request.method.equals(HttpMethods.OPTIONS) && !x.filter(_.isInstanceOf[MethodRejection]).isEmpty) => { | |
val allowedMethods: List[HttpMethod] = x.filter(_.isInstanceOf[MethodRejection]).map(rejection=> { | |
rejection.asInstanceOf[MethodRejection].supported | |
}) | |
ctx.complete(HttpResponse().withHeaders( | |
`Access-Control-Allow-Methods`(OPTIONS, allowedMethods :_*) :: allowOriginHeader :: | |
optionsCorsHeaders | |
)) | |
} | |
}).withHttpResponseHeadersMapped { headers => | |
allowOriginHeader :: headers | |
} | |
} | |
} |
val routes: Route = | |
cors { | |
path("hello") { | |
get { | |
complete { | |
"GET" | |
} | |
} ~ | |
put { | |
complete { | |
"PUT" | |
} | |
} | |
} | |
} |
This is my implementation:
class MyServiceActor extends Actor with MyService {
def actorRefFactory = context
def requestMethodAndResponseStatusAsInfo(req: HttpRequest): Any => Option[LogEntry] = {
case res: HttpResponse => Some(LogEntry(req.method + ":" + req.uri + ":" + res.message.status, InfoLevel))
case _ => None // other kind of responses
}
def routeWithLogging = logRequestResponse(requestMethodAndResponseStatusAsInfo _)(myRoute ~ testRoute)
def receive = runRoute(routeWithLogging)
}
// this trait defines our service behavior independently from the service actor
trait MyService extends HttpService {
implicit val rejectHandler = RejectionHandler {
case MissingQueryParamRejection(paramName) :: _ => ctx
=> ctx.complete(APIresponse.errorResponse(s"Parameter ' $paramName ' is missing", 404))
case MissingFormFieldRejection(paramName) :: _ => ctx
=> ctx.complete(APIresponse.errorResponse(s"Parameter ' $paramName ' is missing", 404))
}
private val allowOriginHeader = Access-Control-Allow-Origin
(AllOrigins)
private val optionsCorsHeaders = List(
Access-Control-Allow-Headers
("Origin, X-Requested-With, Content-Type, Accept, Accept-Encoding, Accept-Language, Host, Referer, User-Agent"),
Access-Control-Max-Age
(1728000))
def cors[T]: Directive0 = mapRequestContext { ctx => ctx.withRouteResponseHandling({
//It is an option requeset for a resource that responds to some other method
case Rejected(x) if (ctx.request.method.equals(HttpMethods.OPTIONS) && !x.filter(_.isInstanceOf[MethodRejection]).isEmpty) => {
val allowedMethods: List[HttpMethod] = x.filter(_.isInstanceOf[MethodRejection]).map(rejection=> {
rejection.asInstanceOf[MethodRejection].supported
})
ctx.complete(HttpResponse().withHeaders(
`Access-Control-Allow-Methods`(OPTIONS, allowedMethods :_*) :: allowOriginHeader ::
optionsCorsHeaders
))
}
}).withHttpResponseHeadersMapped { headers =>
allowOriginHeader :: headers
}
}
val userLogic = new UserMgmtLOGIC
var response = new String
val myRoute: Route =
path("api" / "user-service") {
get {
ctx => ctx.complete(APIresponse.successResponese(null, "Welcome to User Management Service"))
}
} ~ cors {
path("api" / "user-service" / "create-user") {
post {
formFields('email, 'user_name, 'password, 'role?) {(email, user_name, password, role) =>
if(email.isEmpty()){
ctx => ctx.complete(APIresponse.errorResponse("Email field is empty"))
}else if(password.isEmpty()){
ctx => ctx.complete(APIresponse.errorResponse("Password field is empty"))
}else if(user_name.isEmpty()){
ctx => ctx.complete(APIresponse.errorResponse("User Name field is empty"))
}else{
val userData = Map("email" -> email, "user_name" -> user_name, "password" -> password, "role" -> role)
var payload = userData.asInstanceOf[Map[String, Any]]
response = userLogic.createUserRecord(payload)
ctx => ctx.complete(response)
}
}
}
}
} ~ path("api" / "user-service" / "delete-user") {
get {
parameter('email){(email) =>
response = userLogic.deleteUserRecord(email)
ctx => ctx.complete(response)
}
}
} ~ path("api" / "user-service" / "all-users") {
get {
response = userLogic.getAllUsers()
ctx => ctx.complete(response)
}
} ~
cors {
path("api" / "user-service" / "login") {
post {
formFields('username, 'password) {(username, password) =>
if(username.isEmpty()){
ctx => ctx.complete(APIresponse.errorResponse("Email field is empty"))
}else if(password.isEmpty()){
ctx => ctx.complete(APIresponse.errorResponse("Password field is empty"))
}else{
response = userLogic.userLogin(username, password)
ctx => ctx.complete(response)
}
}
}
}
} ~ path("api" / "user-service" / "test") {
get {
respondWithMediaType(application/json
) {
respondWithHeader(RawHeader("Access-Control-Allow-Origin","*")) {
ctx => ctx.complete("""{"name" : "olalekan"}""")
}
}
}
}
}
Can anyone help?
Ignore above comment. Got it working by adding the following to the CORSSupport class...
|| (ctx.request.method.equals(HttpMethods.OPTIONS)
&& x.exists(_.isInstanceOf[AuthenticationFailedRejection]))
as well as adding "Authorization" to the list of headers
Thanks