Patriicya
Patriicya

Reputation: 158

CORS Play Framework

I'm developing an Angular 2 application on top of a Play Framework 2.5 (Java) back-end. If I access my endpoints through browser URL, they work fine. However, calling it from the Angular 2 application shows the error:

XMLHttpRequest cannot load localhost:9000/app/myendpoint. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'localhost:3000' is therefore not allowed access. The response had HTTP status code 500.

I have tryed to follow the documentation and create the filter:

In application.conf:

play.filters {
  cors {
    pathPrefixes = ["/app"]
    allowedOrigins = null
    allowedHttpMethods = null
  }

Filter:

import play.mvc.*;
import play.api.http.DefaultHttpFilters; <- error with this import
import play.filters.cors.CORSFilter;

public class Filters extends DefaultHttpFilters {
    @Inject public Filters(CORSFilter corsFilter) {
        super(corsFilter);
    }
}

In build.sbt:

libraryDependencies ++= Seq(
  ...,
  filters
)

Does anyone knows the true solution? Thank you :)

Working code:

Filters.java (on project's root)

import com.google.inject.Inject;
import com.google.inject.Singleton;    
import play.http.HttpFilters;
import play.mvc.EssentialAction;
import play.mvc.EssentialFilter;
import play.filters.cors.CORSFilter;

@Singleton
public class Filters extends EssentialFilter implements HttpFilters {

    @Inject
    private CORSFilter corsFilter;

    @Override
    public EssentialAction apply(EssentialAction next) {
        return corsFilter.asJava().apply(next);
    }

    @Override
    public EssentialFilter[] filters() {
        EssentialFilter[] result = new EssentialFilter[1];
        result[0] = this;    
        return result;
    }
}

In application.conf:

  #filters += Filters <- yes, it is commented since my Filters is in project's root
libraryDependencies += filters

play.filters {
 cors{
    # allow all paths
    pathPrefixes = ["/"]
    # allow all origins
    allowedOrigins = null
    allowedHttpMethods = ["GET", "POST", "PUT", "DELETE"]
    # allow all headers
    allowedHttpHeaders = null
} ...

In build.sbt:

libraryDependencies ++= Seq(
  ...,
  filters
)

Thank you all! :)

Upvotes: 9

Views: 3296

Answers (6)

Ano
Ano

Reputation: 96

For the sake of others working with Angular 4 front-end and lagom backend project. I have managed to solve this way.

*I added this line below in my build.sbt in both api and impl *

libraryDependencies += filters

In my impl directory, i created folder filters and added the code below

import play.mvc.EssentialFilter;
import play.filters.cors.CORSFilter;
import play.http.HttpFilters;

import javax.inject.Inject;

public class Filters implements HttpFilters {

    @Inject
    CORSFilter corsFilter;

    public EssentialFilter[] filters() {
        return new EssentialFilter[]{corsFilter.asJava()};
    }
}

In my application.conf i added the code that follows

play.filters.hosts {
  # Allow requests to example.com, its subdomains, and localhost:9000.
  allowed = ["localhost:5000", "localhost:9000"]
}

play.http.filters = "....filters.Filters"

play.filters.cors {
  # Filter paths by a whitelist of path prefixes
  pathPrefixes = ["/"]

  # The allowed origins. If null, all origins are allowed.
  allowedOrigins = null
  allowedHttpMethods = ["GET", "POST"]
  allowedHttpHeaders = ["Accept"]
  preflightMaxAge = 3 days
}

After this, i restarted my lagom microservices and it worked like a charm.

Upvotes: 0

jacks
jacks

Reputation: 4783

I remember having the same import issue since I think there were some repackaging changes for Filters in Play somewhere around 2.5.0. And DefaultHttpFilters was replaced by HttpFilters during that refactoring.

Check the source code for the correct path to HttpFilters. Choose the correct tag for the version of Play you are using and then search down the source tree until you find HttpFilters and check the package. This is the path for the Java API for v 2.5.0.

From your post it seems you are using the Java API but are using the import for the Scala version.

The imports for Play 2.5.0 are:-

Scala - import play.api.http.HttpFilters
Java - import play.http.HttpFilters

I'm using Play 2.5.0 (Scala though - not Java) and this import is working fine for me:-

import play.api.http.HttpFilters

From what you have posted I think you should try changing your problematic import to:-

import play.http.HttpFilters

Also, (as others have stated) I assume you have configured the package for your Filters.java file if you have not placed it in the root package.

This is the entry in my application.conf since I am using package filters rather than the default package.:-

play.http.filters = "filters.Filters"

Upvotes: 1

asch
asch

Reputation: 1963

You do not show much from your project, so it is hard to understand, what a problem it may be. I do not think that there is any CORS issue here, because by default no any security filters are active. You can find here, how you can integrate angular and Play.

Upvotes: 0

pacman
pacman

Reputation: 837

I think that custom filter solves your problem:

import javax.inject.Inject

import play.api.http.DefaultHttpFilters
import play.filters.cors.CORSFilter

class Filters @Inject() (corsFilter: CORSFilter)
  extends DefaultHttpFilters(corsFilter)

and in application.conf

play.http.filters = "filters.MyFilters"

configuration like this:

play.filters.cors {
  pathPrefixes = ["/some/path", ...]
  allowedOrigins = ["http://www.example.com", ...]
  allowedHttpMethods = ["GET", "POST"]
  allowedHttpHeaders = ["Accept"]
  preflightMaxAge = 3 days
}

Upvotes: 0

Gonzalo Matheu
Gonzalo Matheu

Reputation: 10104

If it is only for development purposes you might try with a browser plugin to prevent CORS checks:

For instance, cors everywhere on Firefox or Allow-Control-Allow-Origin in Chrome.

Anyway, in production environment you will have to serve JS assets and services from the same host or set Acces-Control-Allow-Origin header with the specific value for your services.

Upvotes: 0

mkorman
mkorman

Reputation: 352

The best practice is to serve both static context and webservice from single origin. For example, for single domain, every URI except /api/* is meant to serve static content and /api/* is a reverse proxy to Java app. You may be specifically interested in Grunt. nginx and Apache could also work.

For example in nginx you specify following configuration:

location /api {
    rewrite /api/(.*) /$1  break;
    proxy_pass http://127.0.0.1:9000;
    proxy_redirect off;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $remote_addr;
}

location / {
    root   /var/www/location;
    index  index.html index.htm;
}

And then you run your Java app listening on localhost, on port 9000. You are able to copy all your static content to location specified after "root" and get it served by nginx. You send all REST request to /api/method/name

Advantage of this solution is solid security and ability to configure SSL easily.

Upvotes: 3

Related Questions