我如何以编程方式将相同的请求发送到Dropwizard和Jersey中的不同方法?

时间:2016-08-16 15:26:39

标签: java rest jersey dropwizard

现在,我在资源中有一些端点。这些端点访问一些数据并将其返回:

@Path("/v1/event")
@Produces({MediaType.APPLICATION_JSON})
public class EventResource {
  private final DataStore dataStore;

  // constructor stuff

  @Timed
  @GET
  @Path("/all/total")
  public String getAll(@Bind({Bind.Params.QUERY}) Params params) throws Exception {
    return dataStore.getEventTotals(params);
  }
}

我们完全修改了数据的存储方式,所以现在我有了一个访问这个新数据存储的资源:

@Path("/v2/event")
@Produces({MediaType.APPLICATION_JSON})
public class NewEventResource {
  private final NewDataStore newDataStore;

  // constructor stuff

  @Timed
  @GET
  @Path("/all/total")
  public MyDataPojo getAll(@Bind({Bind.Params.QUERY}) Params params) throws Exception {
    return newDataStore.getEventTotals(params);
  }
}

我现在要做的是以v1端点使用这两种资源。某些对象将根据传入的getAll对象中的某些参数决定使用哪种Params方法。

原因是我们有一些客户在旧数据存储中有数据,而其他客户在新数据存储中有数据。我们还有一些使用我们的端点的其他项目。将所有其他项目更改为使用v2端点而不是v1端点是不可行或不现实的。

几个想法。我可以这样做:

@Path("/v1/event")
@Produces({MediaType.APPLICATION_JSON})
public class EventResource {
  private final DataStore dataStore;
  private final NewDataStore newDataStore;

  // constructor stuff

  @Timed
  @GET
  @Path("/all/total")
  public String getAllEndpoint(@Bind({Bind.Params.QUERY}) Params params) throws Exception {
    if (customerInNewDataStore(params.getCustomer())) {
      return getEventTotalsNew(params);
    } else {
      return getEventTotalsOld(params);
    }
  }

  private MyDataPojo getEventTotalsNew(Params params) throws Exception {
    return newDataStore.getEventTotals(params);
  }

  private String getEventTotalsOld(Params params) throws Exception {
    return dataStore.getEventTotals(params);
  }
}

问题在于getEvenTotalsNewgetEventTotalsOld会返回不同的类型。我怎么能合并这个?此外,由于我们的代码库中存在相当多的端点,因此对每个端点执行此操作会很麻烦。

我一直在阅读泽西岛的过滤器和拦截器:https://jersey.java.net/documentation/latest/filters-and-interceptors.html

ContainerRequestFilter能够完成我想做的事吗?我可以访问过滤器中的Params params对象吗?

还有其他更好的方法吗?我对所有想法持开放态度。

谢谢!

1 个答案:

答案 0 :(得分:0)

我想我可能有你想要的东西。您可以使用预匹配过滤器来修改请求。

这是基于您的示例,说明您有一个v1和v2 API,两者都相同(除了版本控制)。我正在使用自定义标头进行路由。

考虑这两个资源:

@Path("v1")
@Produces(MediaType.APPLICATION_JSON)
public class TestResource1 {
    @GET
    @Path("test")
    public String get() {
        return "Hello v1";
    }
}

@Path("v2")
@Produces(MediaType.APPLICATION_JSON)
public class TestResource2 {
    @GET
    @Path("test")
    public MyResultObj get() {

        MyResultObj o = new MyResultObj();
        o.name = "pandaa";
        o.message = "Hello V2";

        return o;
    }

    public static class MyResultObj {
        @JsonProperty
        String name;
        @JsonProperty
        String message;
    }
}

这些都与上下文中的版本类型和返回类型的异常相同。注意,在这种情况下,返回类型无关紧要。无论如何,响应中的最终结果是json字符串。在您的情况下,您还可以执行以下操作:

Response.ok(myResultObject).build();

此时,所有返回类型都只是响应。

无论如何,版本1打印的东西,版本2返回一个对象。

预匹配(重要的,否则你无法更改URI)过滤器将如下所示:

@PreMatching
public class RoutingFilter implements ContainerRequestFilter {


    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {

        String headerString = requestContext.getHeaderString("IS_V2");
        boolean v2 = headerString != null && headerString.equals("yes");
        URI absolutePath = requestContext.getUriInfo().getAbsolutePath();

        if(v2) {
            URI v2Redirect = URI.create(absolutePath.toString().replace("v1", "v2"));
            requestContext.setRequestUri(v2Redirect);
        }

    }

}

它只是评估标头并替换URI中的版本。可能有更好的方法来做到这一点,但这又是一个如何处理这个问题的例子。

最后,测试:

artur@pandaadb:~/dev/vpn$ curl "localhost:9085/api/v1/test" --header "IS_V2: yes"
{"name":"pandaa","message":"Hello V2"}

artur@pandaadb:~/dev/vpn$ curl "localhost:9085/api/v1/test" --header "IS_V2: no"
Hello v1

注意两者是如何处理V1的请求。第一个请求虽然在内部重新路由到v2。

您可以编写更通用的版本(因为您可能需要向后兼容,例如v1 - > v2和v2 - > v1),以便人们调用v1或v2并不重要。

最后 - 我不确定这是否是一个很好的解决方案:)我个人可能会写一个委托,如你的例子所示。

我希望有所帮助!

编辑:最后 - 您应该能够使用params对象。但是,这可能会导致您使用请求的输入流。我相信这只能做一次,所以你可能需要在阅读之后设置一个新流。

阿图尔

相关问题