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
Jersey 2 filter uses Container Request Context in Client Request Filter
提问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 - 过滤/修改入站响应
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 ThreadLocal
to communicate between the ContainerRequestFilter
and the ClientRequestFilter
, as you can't assume that client requests made in response to a container request will be on the same thread.
我找到了一个很好的方法来做到这一点,它不用于ThreadLocal
在ContainerRequestFilter
和之间进行通信ClientRequestFilter
,因为您不能假设为响应容器请求而发出的客户端请求将在同一线程上。
The way I achieved this is by setting a property in the ContainerRequestConext
object in the ContainerRequestFilter
. I can then pass the ContainerRequestContext
object (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.
我实现这一点的方法是ContainerRequestConext
在ContainerRequestFilter
. 然后我可以将ContainerRequestContext
对象(明确地或通过依赖注入)传递到我的ClientRequestFilter
. 如果您使用依赖注入(如果您使用的是 Jersey 2,那么您可能使用的是 HK2),那么所有这些都可以在不修改任何资源级逻辑的情况下实现。
Have a ContainerRequestFilter
like 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 ClientRequestFilter
that takes a ContainerRequestContext
in 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 Client
or WebTarget
that 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, ContainerRequestFilter
and ContainerResponseFilter
in one single class. The same is true for client filters, ClientRequestFilter
and ClientResponseFilter
can both be implemented in one single filter implementation.
一种容器,过滤器可以同时实现,ContainerRequestFilter
并ContainerResponseFilter
在一个单独的类。这同样适用于客户端过滤器真正的,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 ThreadLocal
pattern:
但据我所知,你不能混合。相反,您可以使用两个单独的过滤器相互通信,例如使用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
}
}
}