Jen-Ming Chung

Cross-Domain on Jersey RESTful Web Services

Jersey is based on JAX-RS Java community standard, and quite convenient for implementing a RESTful Web Services to serve various flavors of calls in backend. In general, we may use the JSONP workaround with a Javascript callback function to serve the cross-domain request. In Jersey, it provides a better way to achieve it by utilising the ContainerRequestFilter interface. Most of all, we can easily adapt or remove this feature in the Web configuration file.

Here’s an example to resolve Access-Control-Origin not allowed error and you can set those parameters want you want:

src/main/java/com/example/filter/CORSFilter.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.example.filter;

import com.sun.jersey.spi.container.ContainerRequest;
import com.sun.jersey.spi.container.ContainerResponse;
import com.sun.jersey.spi.container.ContainerResponseFilter;

public class CORSFilter implements ContainerResponseFilter {

    @Override
    public ContainerResponse filter(ContainerRequest request,
        ContainerResponse response) {

        response.getHttpHeaders().add("Access-Control-Allow-Origin", "*");
        response.getHttpHeaders().add("Access-Control-Allow-Headers",
            "origin, content-type, accept, authorization");
        response.getHttpHeaders().add("Access-Control-Allow-Credentials","true");
        response.getHttpHeaders().add("Access-Control-Allow-Methods",
            "GET, POST, PUT, DELETE, OPTIONS, HEAD");

        return response;
    }
}

The next step is to define your web.xml like so for filtering the response:

src/main/webapp/WEB-INF/web.xml
1
2
3
4
5
6
7
8
9
10
11
<servlet>
    <servlet-name>Jersey Web Application</servlet-name>
    <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
    ...
    <init-param>
        <param-name>com.sun.jersey.spi.container.ContainerResponseFilters</param-name>
        <param-value>com.example.filter.CORSFilter</param-value>
    </init-param>
    ...
    <load-on-startup>1</load-on-startup>
</servlet>

However, the ajax cross-domain request may accompany with another OPTIONS request, we can observe it in the Tomcat localhost_access log as following:

10.4.128.61 - - [06/Aug/2013:00:00:21 +0800] "OPTIONS /example/rest/logs HTTP/1.0" 200 520
10.4.128.61 - - [06/Aug/2013:00:00:22 +0800] "POST /example/rest/logs HTTP/1.0" 200 34

“preflighted” requests first send an HTTP OPTIONS request header to the resource on the other domain, in order to determine whether the actual request is safe to send.

MOZILLA DEVELOPER NETWORK HTTP Access Control (CORS)

Also the catalina.out throws the message as:

Aug 07, 2013 7:22:55 AM com.sun.jersey.server.wadl.generators.AbstractWadlGeneratorGrammarGenerator attachTypes
INFO: Couldn't find grammar element for class java.io.InputStream

Aug 07, 2013 7:22:55 AM com.sun.jersey.server.wadl.generators.AbstractWadlGeneratorGrammarGenerator attachTypes

INFO: Couldn't find grammar element for class javax.ws.rs.core.Response

I add the corresponding OPTIONS handler for all the @Path resource to solve the errors, however, I have no idea whether there is a better way to do this?

1
2
3
4
5
6
7
8
9
10
@OPTIONS
public Response myResource() {
    return Response.ok().build();
}

@POST
public Response myResource() {
    // do something
    return Response.ok().build();
}

References

Comments