How does JAX-RS match two or more compatible @Path expressions?

With the following two methods

@GET
@Path("/{id: \\d+}")
public MyEntity readSingleById(@PathParam("id") long id) {
}


@GET
@Path("/{name: .+}")
public MyEntity readSingleByName(@PathParam("name") String name) {
}

      

Is there a chance the following query readSingleByName

doesn't match readSingleById

?

GET /1234 HTTP/1.1

      

If so, what can I do? What general rule is indicated?

Sorry I had to check the spec if anyone says so.

+1


source to share


1 answer


"Is there a chance that the following query readSingleByName

doesn't match readSingleById

?"

Test it:

@Path("/ambiguous")
public class AmbiguousResource {

    @GET
    @Path("/{id: \\d+}")
    public Response readSingleById(@PathParam("id") long id) {
        return Response.ok("callById").build();
    }

    @GET
    @Path("/{name: .+}")
    public Response readSingleByName(@PathParam("name") String name) {
        return Response.ok("callByName").build();
    }
}

@Test
public void testGetIt() throws Exception {
    int idCount = 0;
    int nameCount = 0;
    for (int i = 0; i < 10 * 1000; i++) {
        String response = c.target(Main.BASE_URI)
            .path("ambiguous").path("1234").request().get(String.class);
        switch (response) {
            case "callById":
                idCount++;
                break;
            case "callByName":
                nameCount++;
                break;
        }
    }
    System.out.println("Id Count: " + idCount);
    System.out.println("Name Count: " + nameCount);  
}

      

Results:

Jersey 2.13
Number of IDs: 10000
Number of Names: 0

Resteasy 3.0.7
Number of identifiers: 10000
Number of names: 0

Now let's play with it a bit. What happens if we move the positions of the method declaration, that is, "name method" before "id method"

@GET
@Path("/{name: .+}")
public Response readSingleByName(@PathParam("name") String name) {
    return Response.ok("callByName").build();
}

@GET
@Path("/{id: \\d+}")
public Response readSingleById(@PathParam("id") long id) {
    return Response.ok("callById").build();
}

      

Now, if we run the same test, the Jersey result will be the same (id == 10000, name == 0). But with Resteasy we get

Resteasy 3.0.7
Id Count: 0
Number of Names: 10000

So it looks like the behavior at this level of ambiguity is implementation specific. Snippet from JAX-RS specs (at this point "method filtering"):



Sorting E

using the number of alphabetic characters in each member as the primary key (descending order), the number of capturing groups as the secondary key (descending order), and the number of capturing groups with non-standard regexes (i.e. not ([^ /]+?)

) as the tertiary key (descending order)

It basically looks like:

  • Check the number of alphabetic characters. (In your case, no.)
  • Check quantity { }

    (regex or not)
  • Check count { }

    (not regex)

From there the regex should be checked. But he does not say anywhere that all remaining candidate methods should be checked against the "best fit" you hope.

I'm not very familiar with regex, so the concept of determining the "best match" of a regex is over my head. I may be wrong, but this seems to be what Jersey does. I also tested the parameter id

as a String (thinking the type of the parameter had something to do with this), but the same result is always used by the "id method".

Another option, you can make a simple change / or maybe someone can cause a hack and do something like

@GET
@Path("/{id: \\d+}{dummy: (/)?}")
public Response readSingleById(@PathParam("id") long id) {

      

Based on the second sort key (mentioned above) this would make the "id method" always before the "name method" after the sort.

Regardless of what you decide, I will definitely do some rigorous testing.

As far as design is concerned, you should aim for less ambiguous URI schemes, but I can see that you are trying by allowing resource to be opened by name and by id. I personally don't have a strong opinion on this, but you can find a good discussion in REST - multiple URIs for the same resource (???)

+3


source







All Articles