【问题标题】:How JAX-RS matches for two or more compatible @Path expressions?JAX-RS 如何匹配两个或多个兼容的 @Path 表达式?
【发布时间】:2015-01-23 18:29:35
【问题描述】:

通过以下两种方法,

@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

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

抱歉,如果有人这么说,我应该检查一下规范。

【问题讨论】:

  • 为了避免这种混淆和歧义,我总是明确定义每个参数,过滤每个方法,如@Path("/id/{id}")@Path("/name/{name}")

标签: jax-rs


【解决方案1】:

“以下请求是否有可能匹配到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方法”之前的“name方法”会发生什么

@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 规范中的一个 sn-p 状态(在“方法过滤”这一点上):

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

这基本上读作:

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

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

我不擅长正则表达式,因此确定“最佳匹配”正则表达式的概念超出了我的想象。我可能错了,但看来这就是泽西正在做的事情。我还测试了将id 参数设为字符串(认为参数类型可能与它有关),但结果相同,“id 方法”总是被命中。

另一种选择,您可以进行简单的更改/或者有些人可能会调用 hack 并执行类似的操作

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

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

无论您决定什么,我都会确保进行彻底的测试。

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

【讨论】:

  • 另一个选项,如果名称不包含数字,我不明白为什么应该,只是使用\\D+(这是一个否定),用于“名称方法”。这将允许除数字之外的任何内容。 id 请求(包含所有数字)永远不会匹配此
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-08-13
  • 1970-01-01
  • 1970-01-01
  • 2021-06-16
  • 2018-03-21
  • 2022-01-23
  • 1970-01-01
相关资源
最近更新 更多