How JAX-RS matches for two or more compatible @Path expressions?

只谈情不闲聊 提交于 2019-12-19 10:42:36

问题


With 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 any chance that following request matches to readSingleByName not to readSingleById?

GET /1234 HTTP/1.1

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

Sorry I should've checked the spec if anyone say so.


回答1:


"Is there any chance that following request matches to readSingleByName not to readSingleById?"

Let's 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
Id Count: 10000
Name Count: 0

Resteasy 3.0.7
Id Count: 10000
Name Count: 0

Now let's do play with it a bit. What happens if we switch the method declaration positions, i.e. the "name method" before the "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 with Resteasy, we get

Resteasy 3.0.7
Id Count: 0
Name Count: 10000

So it appears the behavior at this level of ambiguity is implementation specific. A snippet from the JAX-RS spec states (at this point of "method filtering"):

Sort E using the number of literal characters in each member as the primary key (descending order), the number of capturing groups as a secondary key (descending order) and the number of capturing groups with non-default regular expressions (i.e. not ([^ /]+?)) as the tertiary key (descending order)

This basically reads as:

  1. Check the number of literal characters. (In your case, none).
  2. Check the number of { }s (regex or not)
  3. Check the number of { }s (non regex)

From there the regex should be checked. But it does not anywhere state that all remaining candidate methods should be checked for "best matching" regex, which is the case you are hoping for.

I'm not great with regex, so the concept of determining a "best matching" regex is over my head. I may be wrong, but it appears this is what Jersey is doing. I tested also making the id parameter a String (thinking maybe the parameter type had something to do with it), but same result, the "id method" is always hit.

Another option, you can make a simple alteration/or maybe some might call 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" to always be in front of the "name method" after the sorting.

Whatever you decide, I would make sure to do thorough testing.

As far as design, you should strive to make the URI schemes less ambiguous, but I can see what you are attempting, allowing a resource to be discovered by name and by id. I personally, don't have a strong opinion about this matter, but you can find a good discussion at REST - multiple URI for the same resource (???)



来源:https://stackoverflow.com/questions/28103978/how-jax-rs-matches-for-two-or-more-compatible-path-expressions

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!