12

I have a REST API which is fairly typical, except that the id's of resources are not integers, but strings, which often contain / characters. So if a customer's id is string/with/slashes then the URI for that customer should be http://localhost/customers/string%2Fwith%2Fslashes. When returning a list of customers, I want to construct that URI with a UriBuilder so I can put it in the href of ATOM-style link elements. But it doesn't quite work; here's a small test class that shows what I mean:

@Path("/customers")
public class JerseyTest {

  @Path("{id}")
  public Customer getCustomer(@PathParam("{id}") String id) {
    return null;
  }

  public static void main(String[] args) {
    buildURI("string#with#hashes"); // => http://localhost/customers/string%23with%23hashes
    buildURI("string/with/slashes"); // => http://localhost/customers/string/with/slashes
  }

  public static void buildURI(String id) {
    UriBuilder builder = UriBuilder.fromUri("http://localhost");
    builder.path(JerseyTest.class).path(JerseyTest.class, "getCustomer");
    URI uri = builder.build(id);
    System.out.println(uri);
  }
}

The #'s get encoded as I would expect but the /'s don't. I tried using builder.build(URLEncoder.encode(id)) instead, but then the UriBuilder encodes the %'s so you get .../string%252Fwith%252Fslashes!

It seems inconsistent to me that it encodes # and % but not /, but I suspect there is a good reason for it which I am not seeing. So my question is:

  1. How can I get UriBuilder to give me .../string%2Fwith%2Fslashes, which is the URI that causes Jersey to call getCustomer with id equal to string/with/slashes? edit: I discovered a way to solve this: builder.buildFromEncoded(URLEncoder.encode(id)). Leaving this question open though, in hopes of getting an answer to the second part...
  2. More generally, why does UriBuilder.build encode some special characters, but not others?

I found How do I encode URI parameter values?, where the accepted answer says "Use UriBuilder." Well, I am using it, but apparently I'm using it incorrectly.

Community
  • 1
  • 1
Tyler
  • 21,762
  • 11
  • 61
  • 90
  • A shot in the dark, but what happens when you use the unicode escaped value: `buildURI("string\u002fwith\u002fslashes")`? – stand Jun 09 '11 at 04:08
  • For info, `URI.toString` method modifies the address. `URI.getPath` don't. Take a look at `com.sun.jersey.api.uri.UriComponent.encode(s, t)`, the transformation to `%7...` is done there. `/` are part of the URI, so, maybe, there is way to escape them. (I keep digging) – yves amsellem Jun 09 '11 at 10:14
  • @stand I'm pretty sure `"string\u002fwith\u002fslashes"` compiles to exactly the same bytecode as `"string/with/slashes"` but it's worth a try. So I tried it; same output. Thanks for the idea though! – Tyler Jun 09 '11 at 17:42
  • @yves I found a solution for part 1 and edited it into the question. Let me know if you find anything related to part 2 though! – Tyler Jun 09 '11 at 18:14
  • The downside of URLEncoder is that it replaces spaces with plus ("foo bar" -> "foo+bar"), where you would expect %20 ("foo bar" -> "foo%20bar") – Daniel Hepper Jul 20 '11 at 07:59

1 Answers1

4

This seem to be a confirmed issue:

http://java.net/jira/browse/JAX_RS_SPEC-70

Your workaround sounds good.

Doug Moscrop
  • 4,479
  • 3
  • 26
  • 46
  • Good find. "According to the Jersey team this is not a bug in Jersey but it is wanted by the JAX-RS specification, because they tried to fix it but noticed that the TCK enforces this weird behaviour." :( – Tyler Sep 01 '11 at 01:59