JAX-RS 如何匹配两个或多个兼容的@Path 表达式?
How JAX-RS matches for two or more compatible @Path expressions?
通过以下两种方法,
@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
如果是这样,我该怎么办?指定的一般规则是什么?
抱歉,如果有人这么说,我应该检查规范。
"Is there any chance that following request matches to readSingleByName
not to 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);
}
结果:
Jersey 2.13
Id Count: 10000
Name Count: 0
Resteasy 3.0.7
Id Count: 10000
Name Count: 0
现在让我们来玩一玩。如果我们切换方法声明位置会发生什么,即 "id method"
之前的 "name 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();
}
现在,如果我们 运行 进行相同的测试,则 Jersey 结果将相同(id == 10000,name == 0)。但是有了 Resteasy,我们得到
Resteasy 3.0.7
Id Count: 0
Name Count: 10000
所以看起来这种模糊级别的行为是特定于实现的。 JAX-RS 规范中的一个片段指出(此时 "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)
这基本上是这样写的:
- 检查文字字符数。 (在你的情况下,none)。
- 检查
{ }
的数量(是否为正则表达式)
- 检查
{ }
的数量(非正则表达式)
从那里应该检查正则表达式。但它并没有在任何地方声明应该检查所有剩余的候选方法的 "best matching" 正则表达式,这是你 希望 for.
的情况
我不太擅长正则表达式,所以确定 "best matching" 正则表达式的概念超出了我的理解。我可能是错的,但看起来这就是 Jersey 正在做的事情。我还测试了将 id
参数设为字符串(认为参数类型可能与它有关),但结果相同,总是命中 "id method"。
另一种选择,你可以做一个简单的alteration/or也许有些人可能会称之为 hack 并做一些类似
的事情
@GET
@Path("/{id: \d+}{dummy: (/)?}")
public Response readSingleById(@PathParam("id") long id) {
基于第二个排序键(如上所述),这将使 "id method" 在排序后始终位于 "name method" 之前。
无论您做出什么决定,我都会确保进行彻底的测试。
就设计而言,您应该努力使 URI 方案不那么模糊,但我可以看到您在尝试什么,允许通过名称和 ID 发现资源。我个人对此事没有强烈的看法,但您可以在 REST - multiple URI for the same resource (???)
找到很好的讨论
通过以下两种方法,
@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
如果是这样,我该怎么办?指定的一般规则是什么?
抱歉,如果有人这么说,我应该检查规范。
"Is there any chance that following request matches to
readSingleByName
not toreadSingleById
?"
我们来测试一下:
@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);
}
结果:
Jersey 2.13
Id Count: 10000
Name Count: 0Resteasy 3.0.7
Id Count: 10000
Name Count: 0
现在让我们来玩一玩。如果我们切换方法声明位置会发生什么,即 "id method"
之前的 "name 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();
}
现在,如果我们 运行 进行相同的测试,则 Jersey 结果将相同(id == 10000,name == 0)。但是有了 Resteasy,我们得到
Resteasy 3.0.7
Id Count: 0
Name Count: 10000
所以看起来这种模糊级别的行为是特定于实现的。 JAX-RS 规范中的一个片段指出(此时 "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)
这基本上是这样写的:
- 检查文字字符数。 (在你的情况下,none)。
- 检查
{ }
的数量(是否为正则表达式) - 检查
{ }
的数量(非正则表达式)
从那里应该检查正则表达式。但它并没有在任何地方声明应该检查所有剩余的候选方法的 "best matching" 正则表达式,这是你 希望 for.
的情况我不太擅长正则表达式,所以确定 "best matching" 正则表达式的概念超出了我的理解。我可能是错的,但看起来这就是 Jersey 正在做的事情。我还测试了将 id
参数设为字符串(认为参数类型可能与它有关),但结果相同,总是命中 "id method"。
另一种选择,你可以做一个简单的alteration/or也许有些人可能会称之为 hack 并做一些类似
的事情@GET
@Path("/{id: \d+}{dummy: (/)?}")
public Response readSingleById(@PathParam("id") long id) {
基于第二个排序键(如上所述),这将使 "id method" 在排序后始终位于 "name method" 之前。
无论您做出什么决定,我都会确保进行彻底的测试。
就设计而言,您应该努力使 URI 方案不那么模糊,但我可以看到您在尝试什么,允许通过名称和 ID 发现资源。我个人对此事没有强烈的看法,但您可以在 REST - multiple URI for the same resource (???)
找到很好的讨论