4

I have a java application server with a REST interface provided by resteasy and I have the CORS filter bellow

@Provider
public class CORSFilter implements ContainerResponseFilter {

    public void filter(ContainerRequestContext cReq, ContainerResponseContext cResp) {
        cResp.getHeaders().add("Access-Control-Allow-Origin", "*");
        cResp.getHeaders().add("Access-Control-Allow-Headers", "origin, content-type, accept, authorization, auth-token");
        cResp.getHeaders().add("Access-Control-Allow-Credentials", "true");
        cResp.getHeaders().add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD");
        cResp.getHeaders().add("Access-Control-Max-Age", "1209600");
    }

}

All requests return with the CORS headers:

OPTIONS 200 OK
Access-Control-Allow-Credentials:"true"
Access-Control-Allow-Headers:"origin, content-type, accept, authorization, auth-token"
Access-Control-Allow-Methods:"GET, POST, PUT, DELETE, OPTIONS, HEAD"
Access-Control-Allow-Origin:"*"
Access-Control-Max-Age:"1209600"
Allow:"HEAD, GET, OPTIONS"
Connection:"keep-alive"
Content-Length:"18"
Content-Type:"text/plain"
Date:"Thu, 15 Jan 2015 15:23:01 GMT"
Server:"WildFly/8"

except when I have an internal exception that returns error code 500:

GET 500 Internal Server Error
Connection:"keep-alive"
Content-Length:"8228"
Content-Type:"text/html; charset=UTF-8"
Date:"Thu, 15 Jan 2015 15:23:01 GMT"

How can I make 500 responses contain those headers?

Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720
ibrabeicker
  • 1,786
  • 2
  • 19
  • 31
  • Note that browsers often don't like `Access-Control-Allow-Origin: *` in combination with `Access-Control-Allow-Credentials` - if you want to allow credentials then you should allow a specific origin rather than `*`. – Ian Roberts Jan 17 '15 at 09:18

2 Answers2

3

Use an ExceptionMapper:

@Provider
public class CorsExceptionMapper implements ExceptionMapper<Exception> {

    @Override
    public Response toResponse(Exception ex) {
        ResponseBuilder responseBuilder = Response.serverError();
        responseBuilder.header("Access-Control-Allow-Origin", "*");
        responseBuilder.header("Access-Control-Allow-Headers", "origin, content-type, accept, authorization, auth-token");
        responseBuilder.header("Access-Control-Allow-Credentials", "true");
        responseBuilder.header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD");
        responseBuilder.header("Access-Control-Max-Age", "1209600");
        return responseBuilder.build();
    }

}

To avoid duplication of headers you should use:

cResp.getHeaders().putSingle() 

In the ContainerResponseFilter.

Dherik
  • 17,757
  • 11
  • 115
  • 164
lefloh
  • 10,653
  • 3
  • 28
  • 50
  • I was going to suggest this, but after testing, what I realized is that if the exception is thrown in a JAX-RS context, it will still go through the response filter. So the headers will still be there. If we add the exception mapper, it will actually double up the headers. If the OP is facing a problem with the headers not appearing, then the exception may be occurring outside the JAX-RS scope and may need to be handled somewhere else. – Paul Samsotha Jan 17 '15 at 04:32
  • Thanks! This is annoying. Maybe the easiest solution is to use `cResp.getHeaders().putSingle()` in the `ContainerResponseFilter`? – lefloh Jan 17 '15 at 04:42
  • That should override it huh? Haven't checked, but seems right. – Paul Samsotha Jan 17 '15 at 04:45
  • Yes: `Set the key's value to be a one item list consisting of the supplied value. Any existing values will be replaced.` – lefloh Jan 17 '15 at 04:50
  • I used your provided code but all parameters were being replicated in the header, so I just omitted the header() calls and it worked – ibrabeicker Jan 19 '15 at 18:23
0

The ExceptionMapper will not work in all scenario's I've found. There seem to be some other container responses for which we need yet another filter type. Especially the response to requests to j_security_check (FORM login) don't get CORS headers added. So instead of using both a ContainerResponseFilter and an ExceptionMapper and still having some responses without CORS headers, I've opted for an Undertow filter. The downside is that it only works on Undertow-based servers (Jboss EAP, Wildfly, Wildfly Swarm), but the upside is that it works for all responses.

I wrote a specific filter for adding CORS headers that is installed as a JBoss Module and is configurable from standalone.xml. Find it here:

undertow-cors-filter

Download the .zip file and unzip it in your Wildfly root folder. Then add a filter configuration to standalone.xml:

<filters>
  <filter name="undertow-cors-filter" class-name="com.stijndewitt.undertow.cors.Filter" module="com.stijndewitt.undertow.cors">
    <param name="urlPattern" value="^http(s)?://([^/]+)(:([^/]+))?(/([^/])+)?/api(/.*)?$" />
    <param name="policyClass" value="com.stijndewitt.undertow.cors.AllowMatching" />
    <param name="policyParam" value="^http(s)?://(www\.)?example\.(com|org)$" />   
  </filter>
</filters>

Add a filter-ref to the host element (still in standalone.xml):

<host name="default-host" alias="localhost">
  <filter-ref name="undertow-cors-filter" />
</host>

This will add CORS headers allowing origins http(s)://(www.)example.(com|org) access to all responses for requests to URLs under path segment "/api".

Change the params in the filter definition to configure the behavior. There are 3 policy classes available, AllowAll, AllowMatching and Whitelist and you can write your own custom policies if needed.

Community
  • 1
  • 1
Stijn de Witt
  • 40,192
  • 13
  • 79
  • 80