Getting cors errors in Scala Play Framework v2.6.x

Viewed 4675

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.

3 Answers

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

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"))
}
Related