Skip to content

Instantly share code, notes, and snippets.

@mitchwongho
Last active June 1, 2018 14:24
Show Gist options
  • Star 10 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save mitchwongho/78cf2ae0276847c9d332 to your computer and use it in GitHub Desktop.
Save mitchwongho/78cf2ae0276847c9d332 to your computer and use it in GitHub Desktop.
Implement CORS Request Headers in PlayFramework 2.3.x Applications

Introduction

I recently needed to apply CORS (Cross Origin Request Scripting) to a project that I'm working on. If found 2 posts that helped to achieve it:

I combined both to solve my problem: I have a User CRUD (Play!) webservice and a (Play!) web app that services a SPA (Single Page Application). The SPA needs to access the CRUD webservice, but to acheive this, I would need to apply some CORS rules on both applications.

Files

CRUD WebService

The build.sbt shows that the filter plugin is required.

libraryDependencies ++= Seq(
	// Add here the specific dependencies for this module:
	filters
)

In the CRUD application's Global.scala file we've created a filter (CorsFilter) which we apply to out Global construct

object Global extends WithFilters(CorsFilter) with GlobalSettings {
  ...
}

object CorsFilter extends Filter {
 
  def apply (nextFilter: (RequestHeader) => Future[Result])(requestHeader: RequestHeader): Future[Result] = {
 
    nextFilter(requestHeader).map { result =>
      result.withHeaders(HeaderNames.ACCESS_CONTROL_ALLOW_ORIGIN -> "*",
        HeaderNames.ALLOW -> "*",
        HeaderNames.ACCESS_CONTROL_ALLOW_METHODS -> "POST, GET, PUT, DELETE, OPTIONS",
        HeaderNames.ACCESS_CONTROL_ALLOW_HEADERS -> "Origin, X-Requested-With, Content-Type, Accept, Referer, User-Agent"
      )
    }
  }
}

Web App

For the web app, we need to include routes for OPTIONS that call on our CORS actions

...
OPTIONS  /                           controllers.Application.options(path="")
OPTIONS  /*path                      controllers.Application.options(path)
...

In our Application file, we define a CORS action to apply the CORS headers

// Adds the CORS header
case class CorsAction[A](action: Action[A]) extends Action[A] {

	def apply(request: Request[A]): Future[Result] = {
		action(request).map(result => result.withHeaders(HeaderNames.ACCESS_CONTROL_ALLOW_ORIGIN -> "*",
		HeaderNames.ALLOW -> "*",
		HeaderNames.ACCESS_CONTROL_ALLOW_METHODS -> "POST, GET, PUT, DELETE, OPTIONS",
		HeaderNames.ACCESS_CONTROL_ALLOW_HEADERS -> "Origin, X-Requested-With, Content-Type, Accept, Referer, User-Agent"
		))
	}

	lazy val parser = action.parser
}

We then can implement the options action as a CORSAction that applies the CORS headers

object Application extends Controller {

	...

	def options(path: String) = CorsAction {
		Action { request =>
			Ok.withHeaders(ACCESS_CONTROL_ALLOW_HEADERS -> Seq(AUTHORIZATION, CONTENT_TYPE, "Target-URL").mkString(","))
		}
	}
}
# Application Routes
# This file defines application routes (Higher priority routes first)
# ~~~~
GET ...
OPTIONS / controllers.Application.options(path="")
OPTIONS /*path controllers.Application.options(path)
package controllers
import play.Play
import play.api.http.HeaderNames
import play.api.mvc._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
object Application extends Controller {
...
def options(path: String) = CorsAction {
Action { request =>
Ok.withHeaders(ACCESS_CONTROL_ALLOW_HEADERS -> Seq(AUTHORIZATION, CONTENT_TYPE, "Target-URL").mkString(","))
}
}
}
// Adds the CORS header
case class CorsAction[A](action: Action[A]) extends Action[A] {
def apply(request: Request[A]): Future[Result] = {
action(request).map(result => result.withHeaders(HeaderNames.ACCESS_CONTROL_ALLOW_ORIGIN -> "*",
HeaderNames.ALLOW -> "*",
HeaderNames.ACCESS_CONTROL_ALLOW_METHODS -> "POST, GET, PUT, DELETE, OPTIONS",
HeaderNames.ACCESS_CONTROL_ALLOW_HEADERS -> "Origin, X-Requested-With, Content-Type, Accept, Referer, User-Agent"
))
}
lazy val parser = action.parser
}
name := "cors-play"
organization := "com.example"
version := "0.1-SNAPSHOT"
scalaVersion := "2.11.1"
lazy val root = (project in file(".")).enablePlugins(PlayScala)
libraryDependencies ++= Seq(
// Add here the specific dependencies for this module:
filters
)
resolvers ++= Common.commonResolvers
import play.api._
import play.api.http.HeaderNames
import play.api.mvc._
import play.api.mvc.Results._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
object Global extends WithFilters(CorsFilter) with GlobalSettings {
// called when a route is found, but it was not possible to bind the request parameters
override def onBadRequest (request: RequestHeader, error: String) = Future.successful(
BadRequest("Bad Request: " + error)
)
}
object CorsFilter extends Filter {
def apply (nextFilter: (RequestHeader) => Future[Result])(requestHeader: RequestHeader): Future[Result] = {
nextFilter(requestHeader).map { result =>
result.withHeaders(HeaderNames.ACCESS_CONTROL_ALLOW_ORIGIN -> "*",
HeaderNames.ALLOW -> "*",
HeaderNames.ACCESS_CONTROL_ALLOW_METHODS -> "POST, GET, PUT, DELETE, OPTIONS",
HeaderNames.ACCESS_CONTROL_ALLOW_HEADERS -> "Origin, X-Requested-With, Content-Type, Accept, Referer, User-Agent"
)
}
}
}
@nishanpatel
Copy link

Hi Mitchell,

Thanks for sharing such a nice article.

I have implemented CORS as you mentioned here in your article. Currently I am working on play framework and angular application and facing “No 'Access-Control-Allow-Origin' header is present on the requested resource” issue. I have mentioned my problem on below URL,

http://stackoverflow.com/questions/32808368/redirect-issue-from-localhost-using-play-framework-angularjs

Can you please help me to resolve the same?

@arpitkulria
Copy link

@nishanpatel: -
By looking above example, You have to add "access-control-allow-origin" in HeaderNames.ACCESS_CONTROL_ALLOW_HEADERS in Global.scala file.

@ankushbindra
Copy link

it really helps thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment