How to enable CORS in Grails 3.0.1

I would like to do cross origin communication using Grails in server side. The only documentation that I found is this one

https://grails.org/plugin/cors

but this is for an old version of Grails. The other documentation that I found is for spring:

https://spring.io/guides/gs/rest-service-cors/

so I added the file SimpleCorsFilter.groovy to init/myproject/ folder, but I don't know how to wire this component into resources.groovy


Solution 1:

So, if you got here using grails 3.2.+ you can use the default way.

Go to your application.yml and add:

grails:
    cors:
        enabled: true

It will add Access-Control-Allow-Origin '*'. If you want something different, look this page

Solution 2:

We used a normal servlet filter with an entry in resources.groovy to solve this problem:

public class CorsFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest req, HttpServletResponse resp, FilterChain chain)
            throws ServletException, IOException {

        String origin = req.getHeader("Origin");

        boolean options = "OPTIONS".equals(req.getMethod());
        if (options) {
            if (origin == null) return;
            resp.addHeader("Access-Control-Allow-Headers", "origin, authorization, accept, content-type, x-requested-with");
            resp.addHeader("Access-Control-Allow-Methods", "GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS");
            resp.addHeader("Access-Control-Max-Age", "3600");
        }

        resp.addHeader("Access-Control-Allow-Origin", origin == null ? "*" : origin);
        resp.addHeader("Access-Control-Allow-Credentials", "true");

        if (!options) chain.doFilter(req, resp);
    }
}

resources.groovy:

beans = {
    corsFilter(CorsFilter)
}

This works with CORS requests using basic authentication. I wrote the Grails 2.x plugin and this seemed easier than getting it to work with Grails 3.

Solution 3:

To be specific, here is some code that works. Notice the interceptor name must match your controller name (here, workRequest), the domain needs to be whatever you are calling from (here, localhost:8081) and it is the before() method you want:

package rest
class WorkRequestInterceptor {
boolean before() { 
    header( "Access-Control-Allow-Origin", "http://localhost:8081" )
    header( "Access-Control-Allow-Credentials", "true" )
    header( "Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE" )
    header( "Access-Control-Max-Age", "3600" )  
    true 
}

boolean after() { true }
}

Solution 4:

I was playing around with the emberjs framework together with a Grails 3.0 rest application when I was hit by the CORS issue. Following the steps in this article http://www.greggbolinger.com/rendering-json-in-grails-for-ember-js/ helped me get further.

The article shows how you can use the new Interceptors to create a CorsInterceptor class which sets the correct headers.

class CorsInterceptor {

  CorsInterceptor() {
    matchAll()
  }

  boolean before() {
    header( "Access-Control-Allow-Origin", "http://localhost:4200" )
    header( "Access-Control-Allow-Credentials", "true" )
    header( "Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE")
    header( "Access-Control-Max-Age", "3600" )
    true
  }

  boolean after() { true }
}

This worked as expected for GET requests, but failed for POST and PUT requests. The reason for this was that an OPTIONS preflight request was sent first to http://localhost:8080/mycontroller/1234, which in my case caused a 404 not found to be returned.

With the help from this answer https://stackoverflow.com/a/31834551 I modified the CorsInterceptor class to this instead:

class CorsInterceptor {

   CorsInterceptor() {
    matchAll()
  }

  boolean before() {
    header( "Access-Control-Allow-Origin", "http://localhost:4200" )
    boolean options = ("OPTIONS" == request.method)
    if (options) {

        header( "Access-Control-Allow-Origin", "http://localhost:4200" )
        header( "Access-Control-Allow-Credentials", "true" )
        header( "Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE")
        header( "Access-Control-Max-Age", "3600" )

        response.status = 200
    }

    true 
  }

  boolean after() { true }

}

Now POST and PUT requests were working.