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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/24995307/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 16:19:58  来源:igfitidea点击:

Jersey 2 filter uses Container Request Context in Client Request Filter

javaweb-servicesjerseyjersey-2.0jersey-client

提问by oggmonster

I have a Jersey 2 Web Service that upon receiving a request, makes another request to another web service in order to form the response for the original request. So, when client "A" makes a request to my web service "B", "B" makes a request to "C" as part of forming the response to "A".

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

A->B->C

A->B->C

I want to implement a filter for a Jersey 2 web service that essentially does this:

我想为 Jersey 2 Web 服务实现一个过滤器,它本质上是这样做的:

  • Client "A" will send a request that has a header like "My-Header:first"

  • When my web service "B" then makes a client request "C", it should append to that header, so it sends a request with this header "My-Header:first,second".

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

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

I want to implement this as a filter so all of my resources don't have to duplicate the logic of appending to the request header.

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

However, in Jersey 2, you get these 4 filters:

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

  • ContainerRequestFilter - Filter/modify inbound requests
  • ContainerResponseFilter - Filter/modify outbound responses
  • ClientRequestFilter - Filter/modify outbound requests
  • ClientResponseFilter - Filter/modify inbound responses
  • ContainerRequestFilter - 过滤/修改入站请求
  • ContainerResponseFilter - 过滤/修改出站响应
  • ClientRequestFilter - 过滤/修改出站请求
  • ClientResponseFilter - 过滤/修改入站响应

Jersey Filter Diagram

泽西过滤器图

I need to use the header from an inbound request, modify it, then use it an outbound request, so essentially I need something that is both a ContainerRequestFilter and a ClientRequestFilter. I don't think implementing both in the same filter will work, as you don't know which Client Request maps to which Container Request, or do you?

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

采纳答案by oggmonster

I found a nice way to do this that doesn't use ThreadLocalto communicate between the ContainerRequestFilterand the ClientRequestFilter, as you can't assume that client requests made in response to a container request will be on the same thread.

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

The way I achieved this is by setting a property in the ContainerRequestConextobject in the ContainerRequestFilter. I can then pass the ContainerRequestContextobject (either explicity or through dependency injection) into my ClientRequestFilter. If you use dependency injection (if you're using Jersey 2 then you are probably using HK2), then all of this can be achieved without modifying any of your resource level logic.

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

Have a ContainerRequestFilterlike this:

ContainerRequestFilter这样的:

public class RequestIdContainerFilter implements ContainerRequestFilter {

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

And a ClientRequestFilterthat takes a ContainerRequestContextin its constructor:

而 a在其构造函数ClientRequestFilter中采用 a 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);
    }
}

Then it's just a case of tying this all together. You will need a factory to create any Clientor WebTargetthat you need:

那么这只是将所有这些联系在一起的情况。您将需要一个工厂来创建任何Client或者WebTarget您需要:

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) {

    }
}

Then register the filter and bind your factory on your main application ResourceConfig:

然后注册过滤器并将您的工厂绑定到您的主应用程序ResourceConfig

public class MyApplication extends ResourceConfig {

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

回答by isnot2bad

A container filter can implement both, ContainerRequestFilterand ContainerResponseFilterin one single class. The same is true for client filters, ClientRequestFilterand ClientResponseFiltercan both be implemented in one single filter implementation.

一种容器,过滤器可以同时实现,ContainerRequestFilterContainerResponseFilter在一个单独的类。这同样适用于客户端过滤器真正的,ClientRequestFilter并且ClientResponseFilter既可以在一个单独的滤波器实现方式来实现。

But you cannot mix as far as I know. Instead, you can have two separate filters that communicate with each other e.g. using ThreadLocalpattern:

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