リクエストを受信すると、元のリクエストに対する応答を形成するために、別のWebサービスに別のリクエストを行うJersey 2Webサービスがあります。したがって、クライアント「A」が私のWebサービス「B」にリクエストを送信すると、「B」は「A」への応答を形成する一環として「C」にリクエストを送信します。
A-> B-> C
基本的にこれを行うJersey2Webサービスのフィルターを実装したいと思います。
クライアント「A」は、「My-Header:first」のようなヘッダーを持つリクエストを送信します
次に、私のWebサービス「B」がクライアント要求「C」を作成すると、そのヘッダーに追加する必要があるため、このヘッダー「My-Header:first、second」を使用して要求を送信します。
これをフィルターとして実装して、すべてのリソースがリクエストヘッダーに追加するロジックを複製する必要がないようにします。
ただし、Jersey 2では、次の4つのフィルターを使用できます。
インバウンドリクエストのヘッダーを使用して変更し、アウトバウンドリクエストを使用する必要があるため、基本的にContainerRequestFilterとClientRequestFilterの両方であるものが必要です。どのクライアントリクエストがどのコンテナリクエストにマップされているかわからないので、同じフィルターに両方を実装してもうまくいくとは思いませんか?
ThreadLocal
を使用せずにContainerRequestFilter
とClientRequestFilter
の間で通信する、これを行うための優れた方法を見つけました。これは、クライアントの要求が応答として行われたとは想定できないためです。コンテナへのリクエストは同じスレッドになります。
これを実現する方法は、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);
}
}
それなら、これをすべてまとめるだけの場合です。必要な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) {
}
}
次に、フィルターを登録し、ファクトリをメインアプリケーションにバインドしますResourceConfig
:
public class MyApplication extends ResourceConfig {
public MyApplication() {
register(RequestIdContainerFilter.class);
register(new AbstractBinder() {
@Override
protected void configure() {
bindFactory(MyWebTargetFactory.class).to(WebTarget.class);
}
}
}
}
コンテナフィルターは、ContainerRequestFilter
とContainerResponseFilter
の両方を1つのクラスに実装できます。クライアントフィルターについても同じことが言えます。ClientRequestFilter
とClientResponseFilter
は両方とも1つのフィルター実装で実装できます。
しかし、私の知る限り、混ぜることはできません。代わりに、相互に通信する2つの別個のフィルターを使用できます。 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
}
}
}