Search code examples
javascriptcorsakka-httpspray-json

CORS with Akka-Http and Spray


I'm trying to send http requests from a local file (client) to my backend server.

After reading countless articles on how to enable CROS (cross-origin-resource-sharing), I'm still getting the error: "Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access. The response had HTTP status code 405."

For my backend server, I use Akka-Http and Spray-Json. As a result, I decided to use akka-http-cors (https://github.com/lomigmegard/akka-http-cors), but that didn't seem to solve the problem either. I understand that I should be using the options directive and 'Access-Control-Allow-Origin'(fileName), but I can't seem to figure out how to use them correctly.

I've attached snippets of my backend and javascript code. If anyone knows how to properly enable CROS between my client and server that would be amazing.

Backend scala-akka-spray code

var signInUrl = 'http://0.0.0.0:8080/user/sign-in';

function sendEntry(form, signType) {
  var jsonString = serializeEntry(form);
  
  var httpRequest = new XMLHttpRequest();
  httpRequest.open('POST', signInUrl, true); // true meanining asynchronous
  httpRequest.setRequestHeader('Content-type', 'application/json');
  httpRequest.send(jsonString);
}


Solution

  • I was able to get this working through the code listed at https://dzone.com/articles/handling-cors-in-akka-http

    Copied here for completion:

    import akka.http.scaladsl.model.HttpMethods._
    import akka.http.scaladsl.model.headers._
    import akka.http.scaladsl.model.{HttpResponse, StatusCodes}
    import akka.http.scaladsl.server.Directives._
    import akka.http.scaladsl.server.directives.RouteDirectives.complete
    import akka.http.scaladsl.server.{Directive0, Route}
    
    import scala.concurrent.duration._
    
    
    /**
      * From https://dzone.com/articles/handling-cors-in-akka-http
      *
      *
      */
    trait CORSHandler {
    
      private val corsResponseHeaders = List(
        `Access-Control-Allow-Origin`.*,
        `Access-Control-Allow-Credentials`(true),
        `Access-Control-Allow-Headers`("Authorization",
          "Content-Type", "X-Requested-With"),
        `Access-Control-Max-Age`(1.day.toMillis)//Tell browser to cache OPTIONS requests
      )
      //this directive adds access control headers to normal responses
      private def addAccessControlHeaders: Directive0 = {
        respondWithHeaders(corsResponseHeaders)
      }
      //this handles preflight OPTIONS requests.
      private def preflightRequestHandler: Route = options {
        complete(HttpResponse(StatusCodes.OK).
                 withHeaders(`Access-Control-Allow-Methods`(OPTIONS, POST, PUT, GET, DELETE)))
      }
      // Wrap the Route with this method to enable adding of CORS headers
      def corsHandler(r: Route): Route = addAccessControlHeaders {
        preflightRequestHandler ~ r
      }
      // Helper method to add CORS headers to HttpResponse
      // preventing duplication of CORS headers across code
      def addCORSHeaders(response: HttpResponse):HttpResponse =
        response.withHeaders(corsResponseHeaders)
    }
    

    Using it as:

    private val cors = new CORSHandler {}
    
    val foo: Route = path("foo") {
        //Necessary to let the browser make OPTIONS requests as it likes to do 
        options {
          cors.corsHandler(complete(StatusCodes.OK))
        } ~ post( cors.corsHandler(complete("in foo request")) )
      }
    

    More details: https://ali.actor/enabling-cors-in-akka-http/