使用Jersey 2.21在REST API请求中的可选参数

我正在玩Jersey 2.21 ,我想知道是否可以有一个“可选”的参数,它可以或不存在于对服务器的请求中。

我想成功访问这两种方法:

 http://localhost:8080/my_domain/rest/api/myMethod/1 http://localhost:8080/my_domain/rest/api/myMethod 

正如您所看到的,我正在尝试使整数( id )参数成为可选参数。

我已经将myMethod声明如下:

 @GET @Path("myMethod/{id}") @Produces(MediaType.APPLICATION_JSON + ";charset=UTF-8") public String myMethod(@PathParam("id") Integer id, @Context HttpHeaders hh) 

这有效:

 http://localhost:8080/my_domain/rest/api/myMethod/1 

这也有效:

 http://localhost:8080/my_domain/rest/api/myMethod/ 

但这不起作用 ,我不明白为什么。 它抛出404 Not Found错误:

 http://localhost:8080/my_domain/rest/api/myMethod 

你能指出我正确的方向来解决这个问题吗? 我不喜欢斜线在我所有的REST方法调用中都是强制性的,并且如果可能的话想要压缩它。

所以在一些涉及Jax-RS中的Optional @PathParam中的一些答案之后,问题是使用这个

 @Path("/myMethod{id: (/\\d+)?}") public Response get(@PathParam("id") int id) {} 

导致/在捕获组中。 因此,当Jersey尝试解析/1 ,它将获得exception并发送404.我们可以使用String,但随后它变得丑陋,因为我们需要摆脱领先/ 自己解析它。

 @Path("/myMethod{id: (/\\d+)?}") public Response get(@PathParam("id") String id) { id = id.replace("/", ""); int parsed = Integer.parseInt(id); } 

我提出的另一个解决方案(适用于OP的那个)是将数字/数字分成两个不同的路径表达式,这样前导/不会在实际的id中捕获,并且在解析时不会失败

 @Path("/method{noop: (/)?}{id: ((?<=/)\\d+)?}") public Response get(@PathParam("id") int id) {} 

{noop: (/)?}捕获可选的/ 。 并且{id: ((?<=/)\\d+)?}使用正面的lookbehind ,当且仅当在它之前有/才允许使用数字( \\d+ )( (?<=/) )。 这是必要的,因为/是可选的。 如果我们没有使用这个断言,那么/myMethod123将被允许​​。

这是一个使用Jersey Test Framework的完整测试用例

 public class OptionalParamTest extends JerseyTest { @Path("optional") public static class Resource { @GET @Path("/method{noop: (/)?}{id: ((?<=/)\\d+)?}") public String get(@PathParam("id") int id) { return String.valueOf(id); } } @Override public ResourceConfig configure() { return new ResourceConfig(Resource.class); } @Test public void should_return_id_1() { Response response = target("optional/method/1").request().get(); System.out.println("status=" + response.getStatus()); assertEquals("1", response.readEntity(String.class)); } @Test public void should_return_id_0_with_no_id() { Response response = target("optional/method").request().get(); assertEquals(200, response.getStatus()); assertEquals("0", response.readEntity(String.class)); } @Test public void should_return_404_with_numbers_and_no_slash() { Response response = target("optional/method12").request().get(); assertEquals(404, response.getStatus()); } @Test public void should_return_404_with_numbers_and_letters() { Response response = target("optional/method/12b").request().get(); assertEquals(404, response.getStatus()); } @Test public void should_return_404_with_only_letters() { Response response = target("optional/method/ab").request().get(); assertEquals(404, response.getStatus()); } } 

这是测试的依赖关系

  org.glassfish.jersey.test-framework.providers jersey-test-framework-provider-grizzly2 ${jersey2.version} test  

编辑

对于测试,最好使用盒装Integer而不是int作为方法参数。 使用前者,您可以进行空检查,而不是接收基元的默认值0

有一种更简单的方法可以做到这一点:

 @GET @Path("myMethod/{id}") public String myMethod(@PathParam("id") Integer id) { } @GET @Path("myMethod") public String myMethod() { return myMethod(null); } 

不需要棘手的正则表达式。