View Javadoc

1   package com.atlassian.plugins.rest.module.filter;
2   
3   import com.sun.jersey.spi.container.ContainerRequest;
4   import com.sun.jersey.spi.container.ContainerRequestFilter;
5   import org.apache.commons.lang.StringUtils;
6   
7   import javax.ws.rs.core.MediaType;
8   import javax.ws.rs.core.MultivaluedMap;
9   import javax.ws.rs.ext.Provider;
10  import java.util.HashSet;
11  import java.util.Set;
12  
13  import static javax.ws.rs.core.HttpHeaders.ACCEPT;
14  import static javax.ws.rs.core.MediaType.APPLICATION_ATOM_XML;
15  import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
16  import static javax.ws.rs.core.MediaType.APPLICATION_XML;
17  import static javax.ws.rs.core.MediaType.TEXT_HTML;
18  import static javax.ws.rs.core.MediaType.TEXT_PLAIN;
19  import static javax.ws.rs.core.MediaType.WILDCARD;
20  
21  /**
22   * <p>This is a filter to "fix" user agents with broken {@code Accept} headers.</p> <p>When a client accepts {@code
23   * text/html} we want it to be first in the header. This is because, it is likely that it is a browser and we then
24   * believe that HTML is the best view to render.</p> <p>This was introduced specifically to make Jersey MVC work with
25   * webkit based browsers which by default have their accept header set to {@code application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*\/*;q=0.5}</p>
26   * <p>The "fix" is to prepend {@code text/html} content type in the {@code Accept} header if it exists anywhere (else)
27   * in the header.</p>
28   * <p/>
29   * <p>When a client accepts {@link MediaType#WILDCARD} (IE) we want to prepend it with {@code text/html}.
30   */
31  @Provider
32  public class AcceptHeaderJerseyMvcFilter implements ContainerRequestFilter
33  {
34      static final Set<String> ACCEPTED_CONTENT_TYPES = new HashSet<String>()
35      {{
36              add(TEXT_PLAIN);
37              add(TEXT_HTML);
38              add(TEXT_HTML);
39              add(APPLICATION_JSON);
40              add(APPLICATION_XML);
41              add(APPLICATION_ATOM_XML);
42          }};
43  
44  
45      public ContainerRequest filter(final ContainerRequest request)
46      {
47          final MultivaluedMap<String, String> requestHeaders = request.getRequestHeaders();
48          final String acceptHeader = requestHeaders.getFirst(ACCEPT); // there should only be one Accept header
49  
50          String fixedHeader = addAppXmlWhenWildcardOnly(acceptHeader);
51          fixedHeader = moveTextHtmlToFront(fixedHeader);
52  
53          if (acceptHeader != null && !acceptHeader.equals(fixedHeader))
54          {
55              requestHeaders.putSingle(ACCEPT, fixedHeader);
56          }
57  
58          return request;
59      }
60  
61      private String addAppXmlWhenWildcardOnly(final String acceptHeader)
62      {
63          if (StringUtils.contains(acceptHeader, WILDCARD))
64          {
65              for (String contentType : ACCEPTED_CONTENT_TYPES)
66              {
67                  if (StringUtils.contains(acceptHeader, contentType))
68                  {
69                      return acceptHeader;
70                  }
71              }
72              return APPLICATION_XML + "," + acceptHeader;
73          }
74          return acceptHeader;
75      }
76  
77      private String moveTextHtmlToFront(final String acceptHeader)
78      {
79          if ((StringUtils.contains(acceptHeader, TEXT_HTML) || StringUtils.contains(acceptHeader, WILDCARD))
80                  && !StringUtils.startsWith(acceptHeader, TEXT_HTML))
81          {
82              return TEXT_HTML + "," + acceptHeader;
83          }
84          return acceptHeader;
85      }
86  }