5

I'm trying to get around a CORS error for a simple "hello world" style REST API in Scala/Play 2.6.x and I have tried everything that I can think of at this point. As far as I can tell there is not a good solution or example to be found on the internet, so even if this should be an easy fix then anyone that has a good solution would really help me out by posting it in full. I am simply trying to send a post request from localhost:3000 (a react application using axios) to localhost:9000 where my Scala/Play framework lives.

THE ERRORS

The error that I am getting on the client-side is the following:

XMLHttpRequest cannot load http://localhost:9000/saveTest.
Response to preflight request doesn't pass access control check:
No 'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'http://localhost:3000' is therefore not allowed
access. The response had HTTP status code 403.

The error that I am getting on the server-side is

success] Compiled in 1s

--- (RELOAD) ---

[info] p.a.h.EnabledFilters - Enabled Filters
(see <https://www.playframework.com/documentation/latest/Filters>):

    play.filters.csrf.CSRFFilter
    play.filters.headers.SecurityHeadersFilter
    play.filters.hosts.AllowedHostsFilter
    play.filters.cors.CORSFilter

[info] play.api.Play - Application started (Dev)
[warn] p.f.c.CORSFilter - Invalid CORS
request;Origin=Some(http://localhost:3000);
Method=OPTIONS;Access-Control-Request-Headers=Some(content-type)

MY CODE

I have the following in my application.conf file

# https://www.playframework.com/documentation/latest/Configuration

play.filters.enabled += "play.filters.cors.CORSFilter"

play.filters.cors {
  pathPrefixes = ["/"]
  allowedOrigins = ["http://localhost:3000", ...]
  allowedHttpMethods = ["GET", "POST", "PUT", "DELETE"]
  allowedHttpHeaders = ["Accept"]
  preflightMaxAge = 3 days
}

I've tried changing pathPrefixes to /saveTest (my endpoint), and tried changing allowedOrigins to simply 'https://localhost'. I've tried changing allowedHttpHeaders="Allow-access-control-allow-origin". I've tried setting allowedOrigins, allowedHttpMethods, and allowedHttpHeaders all to null which, according to the documentation (https://www.playframework.com/documentation/2.6.x/resources/confs/filters-helpers/reference.conf) should allow everything (as should pathPrefixes=["/"]

My build.sbt is the following, so it should be adding the filter to the libraryDependencies:

name := """scalaREST"""
organization := "com.example"

version := "1.0-SNAPSHOT"

lazy val root = (project in file(".")).enablePlugins(PlayScala)

scalaVersion := "2.12.2"

libraryDependencies += guice
libraryDependencies += "org.scalatestplus.play" %% "scalatestplus-play" % "3.1.0" % Test
libraryDependencies += filters

According to documentation available here: https://www.playframework.com/documentation/2.6.x/Filters#default-filters you can set the default filters like this:

import javax.inject.Inject

import play.filters.cors.CORSFilter
import play.api.http.{ DefaultHttpFilters, EnabledFilters }

class Filters @Inject()(enabledFilters: EnabledFilters, corsFilter: CORSFilter)
  extends DefaultHttpFilters(enabledFilters.filters :+ corsFilter: _*)

I'm not sure exactly where that should go in my project - it doesn't say, but from other stackoverflow answers I kind of assume it should go in the root of my directory (that is /app). So that's where I put it.

Finally, there was one exotic stackoverflow response that said to put this class in my controllers and add it as a function to my OK responses

  implicit class RichResult (result: Result) {
    def enableCors =  result.withHeaders(
      "Access-Control-Allow-Origin" -> "*"
      , "Access-Control-Allow-Methods" -> 
        "OPTIONS, GET, POST, PUT, DELETE, HEAD"
        // OPTIONS for pre-flight
      , "Access-Control-Allow-Headers" -> 
        "Accept, Content-Type, Origin, X-Json, 
        X-Prototype-Version, X-Requested-With"
        //, "X-My-NonStd-Option"
      , "Access-Control-Allow-Credentials" -> "true"
    )
  }

Needless to say, this did not work.

WRAP UP

Here is the backend for my current scala project.

https://github.com/patientplatypus/scalaproject1/tree/master/scalarest

Please, if you can, show a full working example of a CORS implementation - I cannot get anything I can find online to work. I will probably be submitting this as a documentation request to the Play Framework organization - this should not be nearly this difficult. Thank you.

6
  • OK....I know it needs to be a 200, but it's not. That's just a barebones restatement of the problem. Considering that I'm getting the error message stating on the front end that it's a CORS issue, I'm pretty sure it's a CORS issue. In no way do I mean to be dismissive, but I'm not sure how this gets me closer to a solution. Commented Aug 8, 2017 at 2:22
  • stackoverflow.com/questions/31975439/… may be relevant Commented Aug 8, 2017 at 2:38
  • I've tried that solution and it did not work. Commented Aug 8, 2017 at 2:44
  • 1
    Have you debugged CORSFilter and figured out based on what it says "invalid origin"? Commented Aug 8, 2017 at 5:34
  • 1
    Sure, what I can tell you though is that I got the whole thing to work with play 2.6 and I did so by debugging the filters, which helped me understand what happens and which settings I needed to tweak. I'm not saying you'll find a bug there, but you'll probably see where something goes south. Commented Aug 8, 2017 at 18:15

3 Answers 3

3

Your preflight request fails because you have a Content-Type header set

Add content-type to allowedHttpHeaders in your application.conf like so

#application.conf

play.filters.cors {
  #other cors configuration
  allowedHttpHeaders = ["Accept", "Content-Type"]
} 
Sign up to request clarification or add additional context in comments.

Comments

1

I had this problem too and I added these code in application.conf

play.filters.enabled += "play.filters.cors.CORSFilter"
play.filters.cors {
  allowedHttpMethods = ["GET", "HEAD", "POST"]
  allowedHttpHeaders = ["Accept", "Content-Type"]"
}

and now everything is OK!

for more info

Comments

0

For playframework version 2.8.x , we can wrap the Response in a function as below -

  def addCorsHeader (response : Result) : Result = {
    response.withHeaders(
      ("Access-Control-Allow-Origin", "*"),
      ("Access-Control-Allow-Methods" , "GET,POST,OPTIONS,DELETE,PUT")
    )
  }

Now in the controller, wrap the Results using the above function.

  val result = myService.swipeOut(inputParsed)
  addCorsHeader(Ok(s"$result row successfully updated. Trip complete"))
}
else {
  addCorsHeader(InternalServerError("POST body is mandatory"))
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.