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

我有一个 Jersey 2 Web 服务,在收到请求后,它会向另一个 Web 服务发出另一个请求,以便形成对原始请求的响应。因此,当客户端“A”向我的Web服务“B”发出请求时,“B”向“C”发出请求,作为形成对“A”的响应的一部分。

A->B->C

我想为泽西岛2 Web服务实现一个过滤器,它基本上可以做到这一点:

  • 客户端“A”将发送一个请求,其中包含类似于“My-Header:first”的标头

  • 当我的 Web 服务“B”发出客户端请求“C”时,它应该附加到该标头,因此它会发送一个带有此标头“My-Header:first,second”的请求。

我想将其实现为筛选器,以便我的所有资源都不必复制追加到请求标头的逻辑。

但是,在泽西岛 2 中,您将获得以下 4 个过滤器:

  • 容器请求过滤器 - 筛选/修改入站请求
  • 容器响应过滤器 - 筛选/修改出站响应
  • 客户端请求筛选 - 筛选/修改出站请求
  • 客户端响应筛选 - 筛选/修改入站响应

Jersey Filter Diagram

我需要使用来自入站请求的标头,对其进行修改,然后将其用作出站请求,因此从本质上讲,我需要同时是 ContainerRequestFilter 和 ClientRequestFilter 的东西。我不认为在同一个过滤器中实现两者会起作用,因为你不知道哪个客户端请求映射到哪个容器请求,或者你呢?


答案 1

我发现了一个很好的方法来做到这一点,它不用于在 和 之间进行通信,因为你不能假设响应容器请求的客户端请求将在同一线程上。ThreadLocalContainerRequestFilterClientRequestFilter

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

有一个这样的:ContainerRequestFilter

public class RequestIdContainerFilter implements ContainerRequestFilter {

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

和 一个在其构造函数中采用一个:ClientRequestFilterContainerRequestContext

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);
            }
        }
    }
}

答案 2

容器筛选器可以在一个类中实现这两种功能。客户端筛选器也是如此,两者都可以在单个筛选器实现中实现。ContainerRequestFilterContainerResponseFilterClientRequestFilterClientResponseFilter

但据我所知,你不能混在一起。相反,您可以有两个单独的过滤器相互通信,例如使用模式: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
        }
    }
}

推荐