gpt4 book ai didi

jersey - 有没有办法自动将 JAX-RS 请求中的传入 HTTP header 传播到传出 JAX-RS 请求?

转载 作者:行者123 更新时间:2023-12-04 04:24:08 30 4
gpt4 key购买 nike

我正在寻找正确的方法(在 Jersey 应用程序中)从传入请求中读取 header ,并将其自动安装到我的应用程序正在使用的 JAX-RS 客户端可能发出的任何传出请求中。

理想情况下,我希望在不污染任何类的内部逻辑的情况下做到这一点,因此通过各种过滤器和拦截器。

对于简单的用例,我可以这样做:我有一个 ClientRequestFilter我在我的 ClientBuilder 上注册的实现,并且该过滤器实现具有:

@Context
private HttpHeaders headers;

...这是一个上下文敏感的代理(根据定义),所以在它的 filter方法它可以引用驱动所有这些的入站请求中存在的 header ,并将它们安装在传出请求中。对于简单的情况,这似乎可以正常工作。

但是,这在异步的情况下会失败:如果我使用 JAX-RS 异步客户端 API 生成一堆 GET s,过滤器仍然被调用,但不能再调用该 headers 上的方法实例变量;泽西提示说,据它所知,我们不再在请求范围内。如果请求范围被定义为每个线程,这是有意义的:产生的 GET s 在某个 Jersey 管理的线程池中运行,而不是在与 headers 所使用的线程池相同的线程上。代理是关联的,因此代理会抛出 IllegalStateException当我的过滤器试图与之交谈时,它到处都是。

我觉得有一些 ContainerRequestFilter 的组合和 ClientRequestFilter即使在异步情况下也应该能够完成工作,但我没有看到它。

最佳答案

我要做的是制作 WebTarget预配置有 ClientRequestFilter 的注入(inject)剂添加标题。最好配置WebTarget这种方式,而不是 Client , 因为 Client是一个昂贵的对象来创建。

我们可以制作WebTarget可使用自定义注解和 InjectionResolver 进行注入(inject).在 InjectionResolver ,我们可以得到ContainerRequest并从中获取标题,我们将其传递给 ClientRequestFilter .

这是在行动

创建自定义注释

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface WithHeadersTarget {
String baseUri();
String[] headerNames() default {};
}

使用自定义 ClientRequestFilter 制作 InjectionResolver
private static class WithHeadersTargetInjectionResolver
implements InjectionResolver<WithHeadersTarget> {

private final Provider<ContainerRequest> requestProvider;
private final Client client;

@Inject
public WithHeadersTargetInjectionResolver(Provider<ContainerRequest> requestProvider) {
this.requestProvider = requestProvider;
this.client = ClientBuilder.newClient();
}

@Override
public Object resolve(Injectee injectee, ServiceHandle<?> handle) {
if (injectee.getRequiredType() == WebTarget.class
&& injectee.getParent().isAnnotationPresent(WithHeadersTarget.class)) {
WithHeadersTarget anno = injectee.getParent().getAnnotation(WithHeadersTarget.class);
String uri = anno.baseUri();
String[] headersNames = anno.headerNames();
MultivaluedMap<String, String> requestHeaders = requestProvider.get().getRequestHeaders();

return client.target(uri)
.register(new HeadersFilter(requestHeaders, headersNames));
}
return null;
}

@Override
public boolean isConstructorParameterIndicator() {
return false;
}

@Override
public boolean isMethodParameterIndicator() {
return false;
}

private class HeadersFilter implements ClientRequestFilter {

private final MultivaluedMap<String, String> headers;
private final String[] headerNames;

private HeadersFilter(MultivaluedMap<String, String> headers, String[] headerNames) {
this.headers = headers;
this.headerNames = headerNames;
}

@Override
public void filter(ClientRequestContext requestContext) throws IOException {
// if headers names is empty, add all headers
if (this.headerNames.length == 0) {
for (Map.Entry<String, List<String>> entry: this.headers.entrySet()) {
requestContext.getHeaders().put(entry.getKey(), new ArrayList<>(entry.getValue()));
}
// else just add the headers from the annotation
} else {
for (String header: this.headerNames) {
requestContext.getHeaders().put(header, new ArrayList<>(this.headers.get(header)));
}
}
}
}
}

这个实现的一件事是它检查一个空的 headerNames。在 @WithHeadersTarget注解。如果它是空的,那么我们只转发所有标题。如果用户指定了一些标题名称,那么它只会转发那些

注册 InjectionResolver
new ResourceConfig()
.register(new AbstractBinder() {
@Override
protected void configure() {
bind(WithHeadersTargetInjectionResolver.class)
.to(new TypeLiteral<InjectionResolver<WithHeadersTarget>>() {
}).in(Singleton.class);
}
})

用它
@Path("test")
public static class TestResource {

@WithHeadersTarget(
baseUri = BASE_URI
headerNames = {TEST_HEADER_NAME})
private WebTarget target;

@GET
public String get() {
return target.path("client").request().get(String.class);
}
}

在本例中,如果 headerNames被忽略,那么它将默认为一个空数组,这将导致所有请求 header 都被转发。

使用 Jersey 测试框架完成测试
import org.glassfish.hk2.api.Injectee;
import org.glassfish.hk2.api.InjectionResolver;
import org.glassfish.hk2.api.ServiceHandle;
import org.glassfish.hk2.api.TypeLiteral;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
import org.glassfish.jersey.filter.LoggingFilter;
import org.glassfish.jersey.server.ContainerRequest;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.Test;

import javax.inject.Inject;
import javax.inject.Provider;
import javax.inject.Singleton;
import javax.ws.rs.GET;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.Path;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.ClientRequestContext;
import javax.ws.rs.client.ClientRequestFilter;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import java.io.IOException;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;

import static org.assertj.core.api.Assertions.assertThat;

public class ForwardHeadersTest extends JerseyTest {

private static final String BASE_URI = "http://localhost:8000";
private static final String TEST_HEADER_NAME = "X-Test-Header";

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface WithHeadersTarget {
String baseUri();
String[] headerNames() default {};
}

@Path("test")
public static class TestResource {

@WithHeadersTarget(
baseUri = BASE_URI
)
private WebTarget target;

@GET
public String get() {
return target.path("client").request().get(String.class);
}
}

@Path("client")
public static class ClientResource {

@GET
public String getReversedHeader(@HeaderParam(TEST_HEADER_NAME) String header) {
System.out.println(header);
return new StringBuilder(header).reverse().toString();
}
}

private static class WithHeadersTargetInjectionResolver
implements InjectionResolver<WithHeadersTarget> {

private final Provider<ContainerRequest> requestProvider;
private final Client client;

@Inject
public WithHeadersTargetInjectionResolver(Provider<ContainerRequest> requestProvider) {
this.requestProvider = requestProvider;
this.client = ClientBuilder.newClient();
}

@Override
public Object resolve(Injectee injectee, ServiceHandle<?> handle) {
if (injectee.getRequiredType() == WebTarget.class
&& injectee.getParent().isAnnotationPresent(WithHeadersTarget.class)) {
WithHeadersTarget anno = injectee.getParent().getAnnotation(WithHeadersTarget.class);
String uri = anno.baseUri();
String[] headersNames = anno.headerNames();
MultivaluedMap<String, String> requestHeaders = requestProvider.get().getRequestHeaders();

return client.target(uri)
.register(new HeadersFilter(requestHeaders, headersNames));
}
return null;
}

@Override
public boolean isConstructorParameterIndicator() {
return false;
}

@Override
public boolean isMethodParameterIndicator() {
return false;
}

private class HeadersFilter implements ClientRequestFilter {

private final MultivaluedMap<String, String> headers;
private final String[] headerNames;

private HeadersFilter(MultivaluedMap<String, String> headers, String[] headerNames) {
this.headers = headers;
this.headerNames = headerNames;
}

@Override
public void filter(ClientRequestContext requestContext) throws IOException {
// if headers names is empty, add all headers
if (this.headerNames.length == 0) {
for (Map.Entry<String, List<String>> entry: this.headers.entrySet()) {
requestContext.getHeaders().put(entry.getKey(), new ArrayList<>(entry.getValue()));
}
// else just add the headers from the annotation
} else {
for (String header: this.headerNames) {
requestContext.getHeaders().put(header, new ArrayList<>(this.headers.get(header)));
}
}
}
}
}

@Override
public ResourceConfig configure() {
return new ResourceConfig()
.register(TestResource.class)
.register(ClientResource.class)
.register(new AbstractBinder() {
@Override
protected void configure() {
bind(WithHeadersTargetInjectionResolver.class)
.to(new TypeLiteral<InjectionResolver<WithHeadersTarget>>() {
}).in(Singleton.class);
}
})
.register(new LoggingFilter(Logger.getAnonymousLogger(), true))
.register(new ExceptionMapper<Throwable>() {
@Override
public Response toResponse(Throwable t) {
t.printStackTrace();
return Response.serverError().entity(t.getMessage()).build();
}
});
}

@Override
public URI getBaseUri() {
return URI.create(BASE_URI);
}

@Test
public void testIt() {
final String response = target("test")
.request()
.header(TEST_HEADER_NAME, "HelloWorld")
.get(String.class);

assertThat(response).isEqualTo("dlroWolleH");
}
}

关于jersey - 有没有办法自动将 JAX-RS 请求中的传入 HTTP header 传播到传出 JAX-RS 请求?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47563661/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com