JAX-RS如何匹配两个或多个兼容的@Path表达式?

时间:2015-01-23 06:08:00

标签: jax-rs

使用以下两种方法

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


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

以下请求是否有可能与readSingleByName匹配而不是readSingleById

GET /1234 HTTP/1.1

如果是这样,我该怎么办?指定的一般规则是什么?

抱歉,如果有人这么说,我应该查看规格。

1 个答案:

答案 0 :(得分:4)

  

“以下请求是否有可能与readSingleByName匹配而不是readSingleById?”

让我们测试一下:

@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);  
}

结果:

  

泽西岛2.13
  身份证人数:10000人   名字数:0

     

Resteasy 3.0.7
  身份证人数:10000人   名字数:0

现在让我们玩一下吧。如果我们切换方法声明位置会发生什么,即“id方法”之前的“名称方法”

@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();
}

现在,如果我们运行相同的测试,Jersey结果将是相同的(id == 10000,name == 0)。但是随着Resteasy,我们得到了

  

Resteasy 3.0.7
  身份证数:0
  名称数量:10000

因此,在这种模糊程度上的行为似乎是特定于实现的。来自JAX-RS规范的片段(在“方法过滤”的这一点上):

  

使用每个成员中的文字字符数作为主键(降序),捕获组的数量作为辅助键(降序)和具有非默认值的捕获组数量来排序E正则表达式(即不是([^ /]+?))作为第三键(降序)

这基本上读作:

  1. 检查文字字符数。 (在你的情况下,没有)。
  2. 检查{ } s(正则表达式与否)
  3. 的数量
  4. 检查{ } s(非正则表达式)
  5. 的数量

    从那里应该检查正则表达式。但它并没有任何地方声明所有剩余的候选方法都应该检查“最佳匹配”正则表达式,这是你希望的情况。

    我对正则表达式不太满意,所以确定“最佳匹配”正则表达式的概念已经超出我的想象。我可能错了,但看起来这就是泽西岛正在做的事情。我测试了id参数是一个字符串(想想可能参数类型与它有关),但同样的结果,“id方法”总是被命中。

    另一种选择,你可以做一个简单的改动/或者有些人可能会打电话给黑客并做一些像

    这样的事情。
    @GET
    @Path("/{id: \\d+}{dummy: (/)?}")
    public Response readSingleById(@PathParam("id") long id) {
    

    基于第二个排序键(如上所述),这将使“id方法”在排序后始终位于“名称方法”的前面。

    无论你决定什么,我都会确保做彻底的测试。

    就设计而言,您应该努力使URI方案不那么模糊,但我可以看到您正在尝试的内容,允许通过名称和ID发现资源。我个人而言,对此事没有强烈的意见,但你可以在REST - multiple URI for the same resource (???)找到一个很好的讨论