可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have a Play 2.0.1 application that I want to call using Javascript hosted on other domains. My Javascript call is failing with:
Origin http://mydomain.com is not allowed by Access-Control-Allow-Origin.
I have found a number of examples of how to set the correct HTTP header in Play 1, but have not found anything for Play 2.0.1. After reading the documentation (http://www.playframework.org/documentation/2.0.2/JavaResponse) I've tried the following just to get things working:
public static Result myJsonWebService() {
...
response().setHeader("Access-Control-Allow-Origin", "*");
return ok(toJson(jsonObject));
}
but my JS web service call is still failing.
What do I need to do to get this working?
回答1:
Just for Scala guys, this is the implementation I'm currently using:
import play.api.mvc._
import scala.concurrent._
import play.api.http.HeaderNames._
/**
* Action decorator that provide CORS support
*
* @author Giovanni Costagliola, Nick McCready
*/
case class WithCors(httpVerbs: String*)(action: EssentialAction) extends EssentialAction with Results {
def apply(request: RequestHeader) = {
implicit val executionContext: ExecutionContext = play.api.libs.concurrent.Execution.defaultContext
val origin = request.headers.get(ORIGIN).getOrElse("*")
if (request.method == "OPTIONS") { // preflight
val corsAction = Action {
request =>
Ok("").withHeaders(
ACCESS_CONTROL_ALLOW_ORIGIN -> origin,
ACCESS_CONTROL_ALLOW_METHODS -> (httpVerbs.toSet + "OPTIONS").mkString(", "),
ACCESS_CONTROL_MAX_AGE -> "3600",
ACCESS_CONTROL_ALLOW_HEADERS -> s"$ORIGIN, X-Requested-With, $CONTENT_TYPE, $ACCEPT, $AUTHORIZATION, X-Auth-Token",
ACCESS_CONTROL_ALLOW_CREDENTIALS -> "true")
}
corsAction(request)
} else { // actual request
action(request).map(res => res.withHeaders(
ACCESS_CONTROL_ALLOW_ORIGIN -> origin,
ACCESS_CONTROL_ALLOW_CREDENTIALS -> "true"
))
}
}
}
To use it just decorate your action in the following way:
def myAction = WithCors("GET", "POST") {
Action { request =>
???
}
}
回答2:
Here is some background information...
- 1) http://www.html5rocks.com/en/tutorials/cors/ - Note that you need to read the bit about "not so simple requests" as JSon falls into this category.
- 2) http://stackoverflow.com/questions/9613210/cors-access-control-allow-origin-despite-correct-headers?rq=1
- 3) http://caniuse.com/#search=cors - Details browsers that support CORS
- 4) http://stackoverflow.com/questions/10748537/access-control-allow-origin-on-playframework (for Play 1 NOT Play 2)
So here is what I implemented:
As not-so-simple-requests (see 1 above) make a pre-flight call, you need to add the following to the routes file:
POST /url_to_json_webservice controllers.myJsonWebServices.myJsonWebService
OPTIONS /url_to_json_webservice controllers.myJsonWebServices.checkPreFlight
and then set up the following method in your controller:
public static Result checkPreFlight() {
response().setHeader("Access-Control-Allow-Origin", "*"); // Need to add the correct domain in here!!
response().setHeader("Access-Control-Allow-Methods", "POST"); // Only allow POST
response().setHeader("Access-Control-Max-Age", "300"); // Cache response for 5 minutes
response().setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); // Ensure this header is also allowed!
return ok();
}
Note i may have set more headers than needed here, so please check which ones you need!!
Also remember to add the following to the end of your controller method that returns the actually JSon result (as in my question above):
public static Result myJsonWebService() {
...
response().setHeader("Access-Control-Allow-Origin", "*");
return ok(toJson(jsonObject));
}
回答3:
A nice way of doing this is by extending the Actions:
package actions;
import play.*;
import play.mvc.*;
import play.mvc.Http.Context;
import play.mvc.Http.Response;
public class CorsAction extends Action.Simple {
public Result call(Context context) throws Throwable{
Response response = context.response();
response.setHeader("Access-Control-Allow-Origin", "*");
//Handle preflight requests
if(context.request().method().equals("OPTIONS")) {
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization, X-Auth-Token");
response.setHeader("Access-Control-Allow-Credentials", "true");
return ok();
}
response.setHeader("Access-Control-Allow-Headers","X-Requested-With, Content-Type, X-Auth-Token");
return delegate.call(context);
}
}
Then in your controllers add this line:
@With(CorsAction.class)
Then all ok() requests will respond with the above headers.
回答4:
Implemented as a Scala Filter (play 2.2.x):
import scala.concurrent.ExecutionContext.Implicits.global
import play.api.mvc._
import play.api.mvc.Results._
import play.api.http.HeaderNames._
object Cors extends Filter {
def apply(next: (RequestHeader) => Future[SimpleResult])(request: RequestHeader): Future[SimpleResult] = {
val origin = request.headers.get(ORIGIN).getOrElse("*")
if (request.method == "OPTIONS") {
val response = Ok.withHeaders(
ACCESS_CONTROL_ALLOW_ORIGIN -> origin,
ACCESS_CONTROL_ALLOW_METHODS -> "POST, GET, OPTIONS, PUT, DELETE",
ACCESS_CONTROL_MAX_AGE -> "3600",
ACCESS_CONTROL_ALLOW_HEADERS -> s"$ORIGIN, X-Requested-With, $CONTENT_TYPE, $ACCEPT, $AUTHORIZATION, X-Auth-Token",
ACCESS_CONTROL_ALLOW_CREDENTIALS -> "true"
)
Future.successful(response)
} else {
next(request).map {
res => res.withHeaders(
ACCESS_CONTROL_ALLOW_ORIGIN -> origin,
ACCESS_CONTROL_ALLOW_CREDENTIALS -> "true"
)
}
}
}
}
object Global extends WithFilters(Cors) {...}