Archive for the ‘REST’ Category

For the REST-purists under us, a resources ending with a slash is not the same as a resource ending without a slash. However, the difference between url://xxxx.com/a and url://xxxx.com/a/ is ignored. The trailing slash is omitted.

When you try to map a resource with a trailing slash using the standard @Path annotation, the methods get mapped to the same endpoint causing an exception.

@RequestScoped
public class TestResource {

 @Path("{path}")
 @GET
 public String test1(@PathParam("path") String path) {
 return "test1";
 }

 @Path("{path}/")
 @GET
 public String test2(@PathParam("path") String path) {
 return "test2";
 }
}

Glassfish answers during the deployment with:

SEVERE: Following issues have been detected: WARNING: A resource model has ambiguous (sub-)resource method for HTTP method GET and input mime-types as defined by @Consumes and @Produces annotations at Java methods … These two methods produces and consumes exactly the same mime-types and therefore their invocation as a resource methods will always fail.

In Glassfish which uses the Jersey under the hood, the slash is also ignored. But there is a workaround. You can use regex in the Path-annotation to map resources ending with a slash. In the example at the end, I use regex to map the paths to the methods. But there is something you should know about the path-matching algorithm. The mapping algorithm uses the following rules:

The JAX-RS specification has defined strict sorting and precedence rules for matching URI expressions and is based on a most specific match wins algorithm. The JAX-RS provider gathers up the set of deployed URI expressions and sorts them based on the following logic:

  1. The primary key of the sort is the number of literal characters in the full URI matching pattern. The sort is in descending order.
  2. The secondary key of the sort is the number of template expressions embedded within the pattern, i.e., {id} or {id : .+}. This sort is in descending order.
  3. The tertiary key of the sort is the number of nondefault template expressions. A default template expression is one that does not define a regular expression, i.e., {id}.

In the following example,  test2 is found before test1 because the regex is longer! We check the trailing slash first. When it fails, it falls back to test1.


@RequestScoped
public class TestResource {

 @Path("{test:.*}")
 @GET
 public String test1(@PathParam("test") String test) {
 return "test1";
 }

 @Path("{test:.*[/]}") // takes precedence
 @GET
 public String test2(@PathParam("test") String test) {
 return "test2";
 }
}

Advertisements