I'm developing a java script client application, in server-side I need to handle CORS, all the services I had written in JAX-RS with JERSEY. My code:
@CrossOriginResourceSharing(allowAllOrigins = true) @GET @Path("/readOthersCalendar") @Produces("application/json") public Response readOthersCalendar(String dataJson) throws Exception { //my code. Edited by gimbal2 to fix formatting return Response.status(status).entity(jsonResponse).header("Access-Control-Allow-Origin", "*").build(); }
As of now, i'm getting error No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8080' is therefore not allowed access.”
Please assist me with this.
Thanks & Regards Buddha Puneeth
There are two ways by which we can enable CORS in JAX-RS. The first and the most basic way is to create a filter to inject necessary response header at run-time in every request. The other one is to manually add an appropriate header in each URL endpoint.
JAX-RS is an specification (just a definition) and Jersey is a JAX-RS implementation. Jersey framework is more than the JAX-RS Reference Implementation. Jersey provides its own API that extend the JAX-RS toolkit with additional features and utilities to further simplify RESTful service and client development.
Note: Make sure to read the UPDATE at the bottom. The original answer includes a "lazy" implementation of the CORS filter
With Jersey, to handle CORS, you can just use a ContainerResponseFilter
. The ContainerResponseFilter
for Jersey 1.x and 2.x are a bit different. Since you haven't mentioned which version you're using, I'll post both. Make sure you use the correct one.
import java.io.IOException; import javax.ws.rs.container.ContainerRequestContext; import javax.ws.rs.container.ContainerResponseContext; import javax.ws.rs.container.ContainerResponseFilter; @Provider public class CORSFilter implements ContainerResponseFilter { @Override public void filter(ContainerRequestContext request, ContainerResponseContext response) throws IOException { response.getHeaders().add("Access-Control-Allow-Origin", "*"); response.getHeaders().add("Access-Control-Allow-Headers", "CSRF-Token, X-Requested-By, Authorization, Content-Type"); response.getHeaders().add("Access-Control-Allow-Credentials", "true"); response.getHeaders().add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD"); } }
If you use package scanning to discover providers and resources, the @Provider
annotation should take care of the configuration for you. If not, then you will need to explicitly register it with the ResourceConfig
or the Application
subclass.
Sample code to explicitly register filter with the ResourceConfig
:
final ResourceConfig resourceConfig = new ResourceConfig(); resourceConfig.register(new CORSFilter()); final final URI uri = ...; final HttpServer httpServer = GrizzlyHttpServerFactory.createHttpServer(uri, resourceConfig);
For Jersey 2.x, if you are having problems registering this filter, here are a couple resources that might help
- Registering Resources and Providers in Jersey 2
- What exactly is the ResourceConfig class in Jersey 2?
import com.sun.jersey.spi.container.ContainerRequest; import com.sun.jersey.spi.container.ContainerResponse; import com.sun.jersey.spi.container.ContainerResponseFilter; @Provider 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", "CSRF-Token, X-Requested-By, Authorization, Content-Type"); response.getHttpHeaders().add("Access-Control-Allow-Credentials", "true"); response.getHttpHeaders().add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD"); return response; } }
web.xml configuration, you can use
<init-param> <param-name>com.sun.jersey.spi.container.ContainerResponseFilters</param-name> <param-value>com.yourpackage.CORSFilter</param-value> </init-param>
Or ResourceConfig
you can do
resourceConfig.getContainerResponseFilters().add(new CORSFilter());
Or package scanning with the @Provider
annotation.
Please note that the above example can be improved. You will need to know more about how CORS works. Please see here. For one, you will get the headers for all responses. This may not be desirable. You may just need to handle the preflight (or OPTIONS). If you want to see a better implemented CORS filter, you can check out the source code for the RESTeasy CorsFilter
So I decided to add a more correct implementation. The above implementation is lazy and adds all the CORS headers to all requests. The other mistake is that being that it is only a response filter, the request is still processes. This means that when the preflight request comes in, which is an OPTIONS request, there will be no OPTIONS method implemented, so we will get a 405 response, which is incorrect.
Here's how it should work. So there are two types of CORS requests: simple requests and preflight requests. For a simple request, the browser will send the actual request and add the Origin
request header. The browser expects for the response to have the Access-Control-Allow-Origin
header, saying that the origin from the Origin
header is allowed. In order for it to be considered a "simple request", it must meet the following criteria:
Accept
Accept-Language
Content-Language
Content-Type
DPR
Save-Data
Viewport-Width
Width
Content-Type
header are: application/x-www-form-urlencoded
multipart/form-data
text/plain
If the request doesn't meet all of these three criteria, a Preflight request is made. This is an OPTIONS request that is made to the server, prior to the actual request being made. It will contain different Access-Control-XX-XX
headers, and the server should respond to those headers with its own CORS response headers. Here are the matching headers:
REQUEST HEADER | RESPONSE HEADER |
---|---|
Origin | Access-Control-Allow-Origin |
Access-Control-Request-Headers | Access-Control-Allow-Headers |
Access-Control-Request-Method | Access-Control-Allow-Methods |
XHR.withCredentials | Access-Control-Allow-Credentials |
With the Origin
request header, the value will be the origin server domain, and the response Access-Control-Allow-Origin
should be either this same address or *
to specify that all origins are allowed.
If the client tries to manually set any headers not in the above list, then the browser will set the Access-Control-Request-Headers
header, with the value being a list of all the headers the client is trying to set. The server should respond back with a Access-Control-Allow-Headers
response header, with the value being a list of headers it allows.
The browser will also set the Access-Control-Request-Method
request header, with the value being the HTTP method of the request. The server should respond with the Access-Control-Allow-Methods
response header, with the value being a list of the methods it allows.
If the client uses the XHR.withCredentials
, then the server should respond with the Access-Control-Allow-Credentials
response header, with a value of true
. Read more here.
So with all that said, here is a better implementation. Even though this is better than the above implementation, it is still inferior to the RESTEasy one I linked to, as this implementation still allows all origins. But this filter does a better job of adhering to the CORS spec than the above filter which just adds the CORS response headers to all request. Note that you may also need to modify the Access-Control-Allow-Headers
to match the headers that your application will allow; you may want o either add or remove some headers from the list in this example.
@Provider @PreMatching public class CorsFilter implements ContainerRequestFilter, ContainerResponseFilter { /** * Method for ContainerRequestFilter. */ @Override public void filter(ContainerRequestContext request) throws IOException { // If it's a preflight request, we abort the request with // a 200 status, and the CORS headers are added in the // response filter method below. if (isPreflightRequest(request)) { request.abortWith(Response.ok().build()); return; } } /** * A preflight request is an OPTIONS request * with an Origin header. */ private static boolean isPreflightRequest(ContainerRequestContext request) { return request.getHeaderString("Origin") != null && request.getMethod().equalsIgnoreCase("OPTIONS"); } /** * Method for ContainerResponseFilter. */ @Override public void filter(ContainerRequestContext request, ContainerResponseContext response) throws IOException { // if there is no Origin header, then it is not a // cross origin request. We don't do anything. if (request.getHeaderString("Origin") == null) { return; } // If it is a preflight request, then we add all // the CORS headers here. if (isPreflightRequest(request)) { response.getHeaders().add("Access-Control-Allow-Credentials", "true"); response.getHeaders().add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD"); response.getHeaders().add("Access-Control-Allow-Headers", // Whatever other non-standard/safe headers (see list above) // you want the client to be able to send to the server, // put it in this list. And remove the ones you don't want. "X-Requested-With, Authorization, " + "Accept-Version, Content-MD5, CSRF-Token, Content-Type"); } // Cross origin requests can be either simple requests // or preflight request. We need to add this header // to both type of requests. Only preflight requests // need the previously added headers. response.getHeaders().add("Access-Control-Allow-Origin", "*"); } }
To learn more about CORS, I suggest reading the MDN docs on Cross-Origin Resource Sharing (CORS)
Remove annotation "@CrossOriginResourceSharing(allowAllOrigins = true)
"
Then Return Response like below:
return Response.ok() .entity(jsonResponse) .header("Access-Control-Allow-Origin", "*") .build();
But the jsonResponse
should replace with a POJO Object!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With