JAX-RS/球衣:客户端代理:如何从上下文而不是方法参数中注入路径参数?

JAX-RS / jersey: client proxy: how to inject path parameter from context instead of method argument?

我正在使用代理模式通过 JAX-RS 和 Jersey 调用 Web 服务。我想在概念上做一些类似的事情......向界面添加@Context注释,如:

@Path("foo/{instance}")
public interface Foo 
{
    @Context private UriInfo uriInfo;

    @POST
    @Path("doit")
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes(MediaType.APPLICATION_JSON)
    public Thing1 doit(Thing2 arg);
}

但是当然不能那样做,它是一个接口而不是 class。但是使用代理模式需要一个接口。我使用代理模式创建客户端:

ClientBuilder builder = ClientBuilder.newBuilder().register(JacksonJsonProvider.class);
MyRequestFilter filter = new MyRequestFilter();
builder = builder.register(filter);
WebTarget target = builder.build().target(url);
Foo serviceClient = WebResourceFactory.newResource(Foo.class, target);

但是,调用 serviceClient.doit(...) 会导致 IllegalStateException: The template variable 'instance' has no value。 我尝试使用 ClientRequestFilter 注入实例变量,但在抛出此异常之前甚至没有调用我的过滤器方法。有没有什么方法可以从客户端注入路径参数,而无需将其作为 @PathParam 参数添加到每个方法?原因是,我已经定义了一个我希望我的代理客户端实现的接口,添加一个参数会更改签名。

在您的 JAX-WS 客户端的 Foo 界面中,为缺少的模板值添加一个 @PathParam

@Path("foo/{instance}")
public interface Foo 
{
    @POST
    @Path("doit")
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes(MediaType.APPLICATION_JSON)
    public Thing1 doit(@PathParam("instance") String instance, Thing2 arg);
}

您显然还需要更改您的客户调用 doit() 的方式以适应这种情况。

Thing1 thing1 = serviceClient.doit("anything", arg);

您的服务器资源(客户端界面所基于的)不需要更改。


警告

这感觉就像 hack/workaround。它可能不是最好的解决方案,我有点惊讶它能起作用。但这是我所知道的唯一让代理拥有非空模板值的方法。

最终,似乎没有办法直接这样做。我还没有找到任何方法从 Jersey 客户端代理的上下文中注入“实例”。即使 Foo 是 class,并且安装了 ClientRequestFilter,在发出错误之前也不会调用过滤器。作为一个可行的解决方案,我基本上采用了@andrewjames 的建议并创建了一个带有额外实例参数的接口。这满足了能够使用 WebTarget 创建代理的要求。然后我创建了一个 class 来实现基本接口(方法中没有实例参数的那个),包装代理实例,并将所有方法调用委托给代理。这并不漂亮,但由于所有这些都来自代码生成工具,因此它既不是特别乏味也不是特别容易出错。