Overview
In this blog, we will talk about how to enable and configure CORS support for Jersey, and more importantly, how to trouble shoot if CORS is not working properly.
As mentioned in the previous blog, we were disappointed to find out Apache CXF CORS support did not work and were pleasantly surprised on how easy
CORS filter has been to setup and configure. We have tested CORS filter against Jersey, RESTeasy, and Apache CXF and it worked for every single one of them.
Enable CORS Support
CORS filter is implemented as a Servlet that must be enabled and configured at the web app's level, inside web.xml file.
Here is a sample web.xml file,
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
<servlet>
<servlet-name>Jersey Root REST Service</servlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>jersey.cors</param-value>
</init-param>
<init-param>
<param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Jersey Root REST Service</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<filter>
<filter-name>CORS</filter-name>
<filter-class>com.thetransactioncompany.cors.CORSFilter</filter-class>
<!-- Note: All parameters are options, if ommitted CORS Filter
will fall back to the respective default values.
-->
<init-param>
<param-name>cors.allowGenericHttpRequests</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>cors.allowOrigin</param-name>
<param-value>*</param-value>
</init-param>
<init-param>
<param-name>cors.supportedMethods</param-name>
<param-value>GET, HEAD, POST, OPTIONS, PUT, DELETE</param-value>
</init-param>
<init-param>
<param-name>cors.supportedHeaders</param-name>
<param-value>Content-Type, X-Requested-With, Accept, Authentication</param-value>
</init-param>
<init-param>
<param-name>cors.exposedHeaders</param-name>
<param-value>X-Test-1, X-Test-2</param-value>
</init-param>
<init-param>
<param-name>cors.supportsCredentials</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>cors.maxAge</param-name>
<param-value>3600</param-value>
</init-param>
</filter>
<filter-mapping>
<!-- CORS Filter mapping -->
<filter-name>CORS</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
Configuring CORS Filter
The default configuration values are good for everything except for the following two fields,
- cors.supportedMethods
- cors.supportedHeaders
These two fields must be checked if CORS filter is not working as one expected.
cors.supportedMethods specifies a list of supported methods and the default value is GET, HEAD, and POST only. We recommend listing all HTTP methods as supported methods.
cors.supportedHeaders lists the set of supported header fields. This set must be expanded if more headers are passed in unexpected. We recommend listing as many headers as possible.
Testing and Troubleshooting
CORS support can be tested through javascript and here is an example,
<html>
<head>
<title>Cors Example</title>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script type="text/javascript" src="log4javascript.js"></script>
<script>
var hello = JSON.stringify({"greeting":"Hello","name":"jersey"});
//alert(hello);
$(document).ready(function() {
//alert('before ajax call');
$.ajax({
headers: {
Authentication : 'Bearer access_token'
},
//this is the php file that processes the data and send mail
//url: "http://localhost:8080/cxf-hello-cors/rest/annotatedGet/hello",
//url: "http://localhost:8080/cxf-hello-cors/service1/time",
// url: "http://localhost:8080/resteasy/tutorial/helloworld",
url: "http://localhost:8080/jersey/hello",
contentType: "application/json",
//GET method is used
type: 'DELETE',
//pass the data
dataType: 'json',
//data: JSON.stringify(hello),
data: hello,
//Do not cache the page
cache: false,
//success
success: function (html) {
//alert(html);
document.getElementById("cors").innerHTML = "Echo: " + html.greeting + "," + html.name;
} ,
error:function (data, status) {
alert(data);
alert(status);
}
});
});
</script>
</head>
<body>
<h1>This is the CORS test page</h1>
<p>Hello, <div id="cors"/>
</body>
</html>
Troubleshooting CORS
We use a combination of Tomcat access log, Firefox Firebug, and Jersey client to troubleshoot CORS support.
CORS relies on header to relay cross origin resource sharing information back to the browser and CORS-supported browser will enforce CORS based on these header fields. When CORS is not working as expected, the majority of the errors happen when Web Services do not pass back the appropriate headers due to permission related issues, like supported headers or supported methods. The best place to look for this type of information is in Tomcat's access log.
Here are some sample entries from the access log,
127.0.0.1 - - [31/May/2012:15:40:42 -0400] "GET /jersey/hello HTTP/1.1" 401 -
127.0.0.1 - name [31/May/2012:15:42:27 -0400] "GET /jersey/hello HTTP/1.1" 200 36
127.0.0.1 - - [31/May/2012:15:42:39 -0400] "GET /jersey/hello HTTP/1.1" 401 -
127.0.0.1 - - [31/May/2012:15:44:18 -0400] "GET /jersey/hello HTTP/1.1" 401 -
127.0.0.1 - - [31/May/2012:15:45:45 -0400] "GET /jersey/hello HTTP/1.1" 401 -
127.0.0.1 - - [31/May/2012:15:46:38 -0400] "GET / HTTP/1.1" 401 -
127.0.0.1 - - [31/May/2012:15:46:52 -0400] "GET /jersey/hello HTTP/1.1" 401 -
0:0:0:0:0:0:0:1%0 - - [31/May/2012:15:47:02 -0400] "OPTIONS /jersey/hello HTTP/1.1" 403 94
127.0.0.1 - - [31/May/2012:15:48:06 -0400] "GET / HTTP/1.1" 401 -
0:0:0:0:0:0:0:1%0 - - [31/May/2012:15:51:23 -0400] "OPTIONS /jersey/hello HTTP/1.1" 200 -
0:0:0:0:0:0:0:1%0 - name [31/May/2012:15:51:23 -0400] "DELETE /jersey/hello HTTP/1.1" 200 36
127.0.0.1 - name [31/May/2012:16:01:12 -0400] "GET /jersey/hello HTTP/1.1" 200 36
Each entry represents an access from the client. The last three entries represent
the following, request URI, HTTP status code, return content length. If CORS is not
working as expected, check the following,
- Make sure there is an entry in the access log that corresponds to the request
- Make sure HTTP status code is correct. If HTTP status code is 403, check CORS filter's
supported methods and supported headers to make sure that both settings are configured
properly
If HTTP code is 200 but CORS is still not working, turn Firebug on and examine the request
pay special attention to response headers,
|
Debugging CORS response with Firebug |
|
|
Make sure the set of Access-Control-* headers present in response.