Jersey 2过滤器在客户端请求过滤器中使用容器请求上下文

时间:2014-07-28 12:29:54

标签: java web-services jersey jersey-2.0 jersey-client

我有一个Jersey 2 Web服务,在收到请求后,向另一个Web服务发出另一个请求,以便形成对原始请求的响应。所以,当客户" A"向我的网络服务提出请求" B"," B"向" C"提出请求作为对" A"。

的回应的一部分

A-> B-&以及c

我想为Jersey 2 Web服务实现一个基本上执行此操作的过滤器:

  • 客户" A"将发送一个具有标题的请求 "我的报头:第一"

  • 当我的网络服务" B"然后提出客户请求" C"它应该 附加到该标头,因此它发送带有此标头的请求 "我的报头:第一,第二"

我想将其作为过滤器实现,因此我的所有资源都不必复制附加到请求标头的逻辑。

但是,在Jersey 2中,你会得到这4个过滤器:

  • ContainerRequestFilter - 过滤/修改入站请求
  • ContainerResponseFilter - 过滤/修改出站响应
  • ClientRequestFilter - 过滤/修改出站请求
  • ClientResponseFilter - 过滤/修改入站响应

Jersey Filter Diagram

我需要使用来自入站请求的标头,修改它,然后使用它作为出站请求,所以基本上我需要的东西既是ContainerRequestFilter又是ClientRequestFilter。我不认为在同一个过滤器中实现两者都会有效,因为你不知道哪个客户端请求映射到哪个容器请求,或者你呢?

2 个答案:

答案 0 :(得分:5)

我找到了一种很好的方法来执行此操作,但不使用ThreadLocalContainerRequestFilterClientRequestFilter之间进行通信,因为您无法假设客户端请求作出响应容器请求将在同一个线程上。

我实现此目的的方法是在ContainerRequestConext中的ContainerRequestFilter对象中设置属性。然后,我可以将ContainerRequestContext对象(显式或通过依赖注入)传递到我的ClientRequestFilter。如果您使用依赖注入(如果您使用的是Jersey 2,那么您可能正在使用HK2),那么所有这些都可以在不修改任何资源级别逻辑的情况下实现。

像这样ContainerRequestFilter

public class RequestIdContainerFilter implements ContainerRequestFilter {

@Override
public void filter(ContainerRequestContext containerRequestContext) throws IOException {
    containerRequestContext.setProperty("property-name", "any-object-you-like");
}

ClientRequestFilter在其构造函数中使用ContainerRequestContext

public class RequestIdClientRequestFilter implements ClientRequestFilter {

    private ContainerRequestContext containerRequestContext;

    public RequestIdClientRequestFilter(ContainerRequestContext containerRequestContext) {
        this.containerRequestContext = containerRequestContext;
    }

    @Override
    public void filter(ClientRequestContext clientRequestContext) throws IOException {
        String value = containerRequestContext.getProperty("property-name");
        clientRequestContext.getHeaders().putSingle("MyHeader", value);
    }
}

然后,这只是将这一切捆绑在一起的案例。您需要工厂来创建所需的任何ClientWebTarget

public class MyWebTargetFactory implements Factory<WebTarget> {

    @Context
    private ContainerRequestContext containerRequestContext;

    @Inject
    public MyWebTargetFactory(ContainerRequestContext containerRequestContext) {
        this.containerRequestContext = containerRequestContext;
    }

    @Override
    public WebTarget provide() {
        Client client = ClientBuilder.newClient();
        client.register(new RequestIdClientRequestFilter(containerRequestContext));
        return client.target("path/to/api");
    }

    @Override
    public void dispose(WebTarget target) {

    }
}

然后注册过滤器并将工厂绑定在主应用程序ResourceConfig上:

public class MyApplication extends ResourceConfig {

    public MyApplication() {
        register(RequestIdContainerFilter.class);
        register(new AbstractBinder() {
            @Override
            protected void configure() {
                bindFactory(MyWebTargetFactory.class).to(WebTarget.class);
            }
        }
    }
}

答案 1 :(得分:3)

容器过滤器可以在一个单独的类中同时实现ContainerRequestFilterContainerResponseFilter。对于客户端过滤器也是如此,ClientRequestFilterClientResponseFilter都可以在一个过滤器实现中实现。

但据我所知,你不能混。相反,您可以拥有两个相互通信的独立过滤器,例如使用ThreadLocal模式:

// Container filter that stores the request context in a ThreadLocal variable
public class MyContainerRequestFilter implements ContainerRequestFilter, ContainerResponseFilter {
    public static final ThreadLocal<ContainerRequestContext> requestContextHolder;

    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {
        requestContextHolder.set(requestContext);
    }

    @Override
    public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
        // clean up after request
        requestContextHolder.remove();
    }
}

// Client request filter that uses the info from MyContainerRequestFilter
public class MyClientRequestFilter implements ClientRequestFilter {
    @Override
    public void filter(ClientRequestContext requestContext) throws IOException {
        ContainerRequestContext containerRequestContext =
            MyContainerRequestFilter.requestContextHolder.get();
        if (containerRequestContext != null) {
            // TODO: use info from containerRequestContext to modify client request
        }
    }
}
相关问题