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

时间:2015-09-24 15:54:28

标签: java rest jersey jersey-2.0

我正在玩Jersey 2.21并且我想知道是否可以选择"可选"可以或不存在于服务器请求中的param。

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

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方法调用中强制使用斜杠,并且如果可能的话,我希望将其压缩。

2 个答案:

答案 0 :(得分:11)

所以在对Optional @PathParam in Jax-RS中的一些答案进行了一些讨论后,问题是使用这个

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

导致/位于捕获组中。因此,当Jersey尝试解析/1时,它将获得一个异常并发送一个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+)?}使用positive 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());
    } 
}

这是测试的依赖性

<dependency>
    <groupId>org.glassfish.jersey.test-framework.providers</groupId>
    <artifactId>jersey-test-framework-provider-grizzly2</artifactId>
    <version>${jersey2.version}</version>
    <scope>test</scope>
</dependency>

修改

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

答案 1 :(得分:9)

有一种方式更简单的方法:

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

@GET
@Path("myMethod")
public String myMethod() {
  return myMethod(null);
}

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