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

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.

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"]
}

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

Scala Play app not loading forms

I have a simple setup:
routes:
# creates new ticket
PUT /projects/:projectId/tickets controllers.ProjectsController.add(projectId)
App Controller code looks like:
case class TicketData(ticketId: Option[String], ticketName: String, ticketDescription: String)
val addUpdateForm = Form(
mapping(
"ticketId" -> optional(text),
"ticketName" -> text,
"ticketDescription" -> text
)(TicketData.apply)(TicketData.unapply))
def add(projectId: String) = Action { implicit request =>
val ticket = addUpdateForm.bindFromRequest.bindFromRequest.get
Ok(Json.toJson(cassandraClient.addTicket(projectId, ticket.ticketName, ticket.ticketDescription)))
}
When I try to send a req from postman (tried several combos, I have no oauth) I just always get 403 ... there is nothing really useful in logs:
[debug] i.n.u.i.JavassistTypeParameterMatcherGenerator - Generated: io.netty.util.internal.__matchers__.io.netty.channel.ChannelMatcher
[debug] i.n.u.i.JavassistTypeParameterMatcherGenerator - Generated: io.netty.util.internal.__matchers__.io.netty.handler.codec.http.HttpObjectMatcher
[debug] i.n.u.i.JavassistTypeParameterMatcherGenerator - Generated: io.netty.util.internal.__matchers__.io.netty.handler.codec.http.HttpContentMatcher
I'm missing something here and have no idea what to be honest.
It looks like you have a problem with the CORS configuration.
To verify it just allow every request (in application.conf):
play.filters.cors {
pathPrefixes = ["/"]
allowedOrigins = null
allowedHttpMethods = ["GET", "POST", "PUT", "DELETE"]
allowedHttpHeaders = null
}
This post looks very similar:
Trouble-shooting CORS in Play Framework 2.4.x

Haskell Yesod - CORS problems with browsers OPTIONS requests when doing POST requests

I have used Network.Wai.Middleware.Cors's simpleCors, it worked properly for GET requests, but when I try to make a POST request I get the following problem
OPTIONS /users
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Status: 400 Bad Request 0.032443s
The only way I was able to make it work was by removing the simpleCors from the following part in Application.hs
-- | Convert our foundation to a WAI Application by calling #toWaiAppPlain# and
-- applying some additional middlewares.
makeApplication :: App -> IO Application
makeApplication foundation = do
logWare <- makeLogWare foundation
-- Create the WAI application and apply middlewares
appPlain <- toWaiAppPlain foundation
return $ logWare $ defaultMiddlewaresNoLogging $ simpleCors $ appPlain
and adding a OPTIONS method response
optionsNewUserR :: Handler RepPlain
optionsNewUserR = do
return $ RepPlain $ toContent ("" :: Text)
and adding CORS headers... But it is a dirty solution, because I would need to change ALL my API handlers! Any help is highly appreciated!
I believe the issue is that simpleCors is built off simpleCorsResourcePolicy, which only covers simpleMethods, which doesn't cover OPTIONS.
You can fix this issue by using the same methods to roll whatever middleware you need.
Here's the one I use for the OPTIONS problem you've described:
{-# LANGUAGE OverloadedStrings #-}
module Middlewares where
import Network.Wai (Middleware)
import Network.Wai.Middleware.AddHeaders (addHeaders)
import Network.Wai.Middleware.Cors (CorsResourcePolicy(..), cors)
-- | #x-csrf-token# allowance.
-- The following header will be set: #Access-Control-Allow-Headers: x-csrf-token#.
allowCsrf :: Middleware
allowCsrf = addHeaders [("Access-Control-Allow-Headers", "x-csrf-token,authorization")]
-- | CORS middleware configured with 'appCorsResourcePolicy'.
corsified :: Middleware
corsified = cors (const $ Just appCorsResourcePolicy)
-- | Cors resource policy to be used with 'corsified' middleware.
--
-- This policy will set the following:
--
-- * RequestHeaders: #Content-Type#
-- * MethodsAllowed: #OPTIONS, GET, PUT, POST#
appCorsResourcePolicy :: CorsResourcePolicy
appCorsResourcePolicy = CorsResourcePolicy {
corsOrigins = Nothing
, corsMethods = ["OPTIONS", "GET", "PUT", "POST"]
, corsRequestHeaders = ["Authorization", "Content-Type"]
, corsExposedHeaders = Nothing
, corsMaxAge = Nothing
, corsVaryOrigin = False
, corsRequireOrigin = False
, corsIgnoreFailures = False
}
And then just compose the middlewares you need like you're already doing:
run port $ logger . allowCsrf . corsified $ app cfg
Simplifying the answer of Mario we can use simplePolicy
import Network.Wai.Middleware.Cors
allowCors :: Middleware
allowCors = cors (const $ Just appCorsResourcePolicy)
appCorsResourcePolicy :: CorsResourcePolicy
appCorsResourcePolicy =
simpleCorsResourcePolicy
{ corsMethods = ["OPTIONS", "GET", "PUT", "POST"]
, corsRequestHeaders = ["Authorization", "Content-Type"]
}
And to use it:
main = scotty 8080 $ do
middleware allowCors
matchAny "/" $ text "Success"

Play Framework CORS Headers

I'm trying to set CORS Headers for my play framework app. Specifically I'm getting this error
cannot load http://127.0.0.1:9000/. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:9000' is therefore not allowed access.
I figured I could easily handle this by following these instructions:
https://www.playframework.com/documentation/2.5.x/CorsFilter
However, after doing this. nothing has changed.
curl -I localhost:9000/
HTTP/1.1 200 OK
Content-Length: 4540
Content-Type: text/html; charset=utf-8
Date: Mon, 11 Jul 2016 20:03:33 GMT
My conf is:
play.http.filters = "global.Filters"
play.filters.cors {
allowedOrigins = ["http://www.example.com", "*"]
allowedHttpMethods = ["GET", "POST"]
allowedHttpHeaders = ["Accept"]
}
and my Filters.scala file is:
package global
import javax.inject.Inject
import play.api.http.DefaultHttpFilters
import play.filters.cors.CORSFilter
class Filters #Inject() (corsFilter: CORSFilter)
extends DefaultHttpFilters(corsFilter)
If someone could tell me why the filters don't seem to be getting applied to the responses, that'd be great.
Play filters are enticing, but when they do not work as expected, as you noticed, the magic is not that easy to track down.
I prefer to use something like this:
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"
)
}
Then you can easily invoke it in your response like this:
Ok(Json.obj("ok" -> "1")).enableCors
It's easy to understand, can be placed only where you want to enable CORS, and very easy to debug!
I would not recommend writing/using any code to enable CORS which is basically a framework feature and only needs configuration.
The stuff you copied from the documentation is correct:
cors.conf where you modify the play.filters.cors settings. But you seem to have misconfigured something, e.g. the allowedOrigin = * should be configured as null in the config. (Have a look at the documentation page and the linked reference.conf)
# The allowed origins. If null, all origins are allowed.
play.filters.cors.allowedOrigins = null
You have correctly enabled the CORSFilter in your Filters.scala
Now test your configuration with a correct cURL CORS request:
curl -H "Origin: http://example.com" \
-H "Access-Control-Request-Method: GET" \
-H "Access-Control-Request-Headers: X-Requested-With" \
-X OPTIONS --verbose \
http://localhost:9000/
for me it worked after one day (maybe cash or other things)
application.conf:
play.http.filters = "filters.Filters"
play.filters.cors {
# allow all paths
pathPrefixes = ["/"]
# allow all origins (You can specify if you want)
allowedOrigins = null
allowedHttpMethods = ["GET", "POST"]
# allow all headers
allowedHttpHeaders = null
}
build.sbt :
val appDependencies = Seq(
filters,
....
)
in package filters.Filter :
package filters;
import javax.inject.Inject;
import play.mvc.EssentialFilter;
import play.filters.cors.CORSFilter;
import play.http.DefaultHttpFilters;
public class Filters extends DefaultHttpFilters {
CORSFilter corsFilter;
#Inject
public Filters(CORSFilter corsFilter) {
super(corsFilter);
this.corsFilter = corsFilter;
}
public EssentialFilter[] filters() {
return new EssentialFilter[] { corsFilter.asJava() };
}
}
and in my ajax call:
$.ajax({
method:'GET',
url: xxxxxxxx',
dataType: 'json',
headers: {'url': yyyyy,
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, PUT',
'Access-Control-Allow-Headers': 'Content-Type'
},
success: function(data) {
....});
i have no more error, in prod and in local environment !! thank you all

Get all active Routes / Paths in a running Play application

Is there a standard way to get all possible (excluding wildcards of course) routes / paths valid within a play application ?
I can do it with
Play.current.routes.map( _.documentation.map(_._2))
which gives me all available routes but it looks a bit hacky to me.
Just for elaboration, what I do is this ( exactly what you did ):
val myroutes = Play.current.routes map (routes => routes.documentation) getOrElse (Nil)
myroutes foreach { r =>
println("%-10s %-50s %s".format(r._1, r._2, r._3))
}
And I also get:
GET / controllers.Application.index
GET /apis controllers.Application.apis
GET /docs controllers.Application.docs
GET /sampleapi/status/$id<[^/]+> controllers.SampleAPI.status(id:String)
GET /city/$id<[^/]+> controllers.CityAPI.findById(id:Long)
GET /city1/$id<[^/]+> controllers.CityAPI.findById1(id:Long)
GET /apps/authorize/ controllers.Apps.authorize
POST /apps/send_auth/ controllers.Apps.send_auth
GET /clients/ controllers.Clients.list
GET /client/new controllers.Clients.create
POST /client/add controllers.Clients.add
POST /client/update controllers.Clients.update
GET /client/edit/$id<[^/]+> controllers.Clients.edit(id:String)
PUT /client/ controllers.Clients.update
GET /client/$id<[^/]+> controllers.Clients.get(id:String)
DELETE /client/$id<[^/]+> controllers.Clients.delete(id:String)
GET /login controllers.Auth.login
POST /authenticate controllers.Auth.authenticate
GET /logout controllers.Auth.logout
POST /oauth2/access_token controllers.OAuth2Controller.accessToken
GET /webjars/$file<.+> controllers.WebJarAssets.at(file:String)
GET /assets/$file<.+> controllers.Assets.at(path:String = "/public", file:String)
This also includes webjar and assets path, so you may want to filter them out.

Performing a simple HTTP GET with Dispatch

The following is a valid query in a browser (e.g. Firefox):
http://www.freesound.org/api/sounds/search/?q=barking&api_key=074c0b328aea46adb3ee76f6918f8fae
yielding a JSON document:
{
"num_results": 610,
"sounds": [
{
"analysis_stats": "http://www.freesound.org/api/sounds/115536/analysis/",
"analysis_frames": "http://www.freesound.org/data/analysis/115/115536_1956076_frames.json",
"preview-hq-mp3": "http://www.freesound.org/data/previews/115/115536_1956076-hq.mp3",
"original_filename": "Two Barks.wav",
"tags": [
"animal",
"bark",
"barking",
"dog",
"effects",
...
I am trying to perform this query with Dispatch 0.9.4. Here's a build.sbt:
scalaVersion := "2.10.0"
libraryDependencies += "net.databinder.dispatch" %% "dispatch-core" % "0.9.4"
From sbt console, I do the following:
import dispatch._
val q = url("http://www.freesound.org/api/sounds/search")
.addQueryParameter("q", "barking")
.addQueryParameter("api_key", "074c0b328aea46adb3ee76f6918f8fae")
val res = Http(q OK as.String)
But the promise always completes with the following error:
res0: dispatch.Promise[String] = Promise(!Unexpected response status: 301!)
So what am I doing wrong? Here is the API documentation in case it helps.
You can enable redirect following with the configure method on the Http executor:
Http.configure(_ setFollowRedirects true)(q OK as.String)
You could also pull the Location out of the 301 response manually, but that's going to be a lot less convenient.