January 17, 2013

JAX-RS / Jersey CORS Filter and JAXB List


I had to implement CORS functionnalities for cross domain javascript in my REST app.
I stumbled upon the excellent solution posted by Usul.

Basically, you have to implement a ContainerResponseFilter that will check request headers and set response headers :
 public ContainerResponse filter(ContainerRequest req, ContainerResponse contResp) {
 
        ResponseBuilder resp = Response.fromResponse(contResp.getResponse());
        resp.header("Access-Control-Allow-Origin", "*")
                .header("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
 
        String reqHead = req.getHeaderValue("Access-Control-Request-Headers");
 
        if(null != reqHead && !reqHead.equals(null)){
            resp.header("Access-Control-Allow-Headers", reqHead);
        }
 
        contResp.setResponse(resp.build());
            return contResp;
    }

This solution worked great until I had to return a List of objects. In this particular case, the Response.fromResponse method returned a ResponseBuilder object in which I had lost my collection type, resulting in a nice exception saying that no MessageBodyWriter was found.

A message body writer for Java class java.util.ArrayList, and Java type class java.util.ArrayList, and MIME media type application/json was not found

To bypass it, instead of using the nice Response.fromResponse method, I had to set the headers "manually" in the ContainerResponse object by accessing the header map :

public ContainerResponse filter(ContainerRequest req, ContainerResponse contResp) {
  contResp.getHttpHeaders().add("Access-Control-Allow-Origin", "*");
  contResp.getHttpHeaders().add("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
  
  String reqHead = req.getHeaderValue("Access-Control-Request-Headers");

  if (null != reqHead && !reqHead.equals(null)) {
   contResp.getHttpHeaders().add("Access-Control-Allow-Headers", reqHead);
  }
  return contResp;
 }

Doing it this way, response object isn't modified and type information isn't lost. JAXB is now able to marshall json or xml either in single object or collection case of return.