如何让 OkHttp 从 Jersey 获取参数并在拦截器中使用?
How can I make OkHttp get a parameter out of Jersey and use in Interceptor?
将 Dropwizard+Jersey 用于网络应用程序,用户使用 username/password 登录并创建带有 user-id 的 cookie。他们在每个请求中发回这个 user-id cookie。
我可以在泽西岛买到这个饼干。问题是当我想涉及 Retrofit2 和 OkHttp 时。
public interface Interceptor {
Response intercept(Chain chain) throws IOException;
interface Chain {
Request request();
Response proceed(Request request) throws IOException;
/**
* Returns the connection the request will be executed on. This is only available in the chains
* of network interceptors; for application interceptors this is always null.
*/
@Nullable Connection connection();
}
}
所以Request
需要包含User-Id
现在的问题是,我们如何让 OkHttp 从 Jersey 得到这个 User-Id?
发送到RequestInterceptor时需要同步。
我没有看到将东西注入 RequestInterceptor 的简单方法
因为它是 Okhttp。问题是 Jersey 是 Java 1900-2000,其中 Retrofit 和 OkHttp 就像 Java 2015+
所以在一个地方做到这一点的唯一方法 User-Id
是使用 RequestInterceptor
它是一个接口,必须共享,所以 User-Id 必须来自 parameter
内部的 intercept
函数,而不是来自构造函数。
该参数需要有 User-Id
这样我们就不必做像 ThreadLocal 或同步这样令人讨厌的事情。
更新:我做了一个示例项目:
https://github.com/andrewarrow/web-wash
如果您查看此文件:
目标是能够替代:
userClient.getSomething(user_id)
和
userClient.getSomething()
并让 userClient auto-magically 在线程中获取 user_id
安全的方式。请记住:
https://github.com/andrewarrow/web-wash/blob/master/src/team/higher/web/client/UserClient.kt
@GET("user/test")
fun getSomething(@Header("User-Id") id: String): String
将使用@Header 中的 id 导致 OKHttp 和 Retrofit2 生成一个
URL 连接并将该 id 放在该 http GET 的 http header 中
到 api:
User-Id must somehome come from parameter inside intercept function not from constructor. That parameter needs to have User-Id so we don't have to do nasty things like ThreadLocal, or synchronize.
使用代理可以解决这个问题。使用 Jersey,您可以为用户 ID 创建一个小包装器,然后让 Jersey 代理它。我们可以使用 javax.inject.Provider
来在拦截器中延迟检索用户。当我们这样做时,用户将被绑定到请求的上下文(这是有保证的,我们不需要担心管理我们自己的 ThreadLocals 或任何东西)。
public class UserIdInterceptor implements Interceptor {
private final Provider<User> userProvider;
UserIdInterceptor(Provider<User> userProvider) {
this.userProvider = userProvider;
}
@Override
public Response intercept(Chain chain) throws IOException {
final User user = userProvider.get();
if (user.isValid()) {
return chain.proceed(chain.request().newBuilder()
.addHeader("User-Id", userProvider.get().getName())
.build());
} else {
return chain.proceed(chain.request());
}
}
}
我们要做的是使用 Jersey Factory
在请求范围内创建 User
。这样我们就可以为每个请求创建一个带有 cookie 的新用户。
public class UserFactory implements Factory<User> {
@Inject
private Provider<ContainerRequest> request;
@Override
public User provide() {
Cookie cookie = request.get().getCookies().get("User-Id");
return cookie != null
? new User(cookie.getValue())
: new User(null);
}
}
我们还将为 Retrofit
创建一个工厂。这样我们就可以将 Provider<User>
注入到工厂中,并在构造它时将其传递给拦截器。
public class RetrofitFactory implements Factory<Retrofit> {
private final Retrofit retrofit;
@Inject
private RetrofitFactory(Provider<User> userProvider,
BaseUrlProvider urlProvider) {
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new UserIdInterceptor(userProvider))
.build();
this.retrofit = new Retrofit.Builder()
.baseUrl(urlProvider.baseUrl)
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build();
}
@Override
public Retrofit provide() {
return this.retrofit;
}
}
然后将它们绑定在一起 AbstractBinder
@Override
public void configure() {
bindFactory(RetrofitFactory.class)
.to(Retrofit.class)
.in(Singleton.class);
bindFactory(UserFactory.class)
.to(User.class)
.in(RequestScoped.class)
.proxy(true);
bindFactory(ClientFactories.MessageClientFactory.class)
.to(MessageClient.class);
bind(new BaseUrlProvider(this.baseUrl))
.to(BaseUrlProvider.class);
}
我在这个 GitHub Repo
中整理了一个完整的演示
将 Dropwizard+Jersey 用于网络应用程序,用户使用 username/password 登录并创建带有 user-id 的 cookie。他们在每个请求中发回这个 user-id cookie。
我可以在泽西岛买到这个饼干。问题是当我想涉及 Retrofit2 和 OkHttp 时。
public interface Interceptor {
Response intercept(Chain chain) throws IOException;
interface Chain {
Request request();
Response proceed(Request request) throws IOException;
/**
* Returns the connection the request will be executed on. This is only available in the chains
* of network interceptors; for application interceptors this is always null.
*/
@Nullable Connection connection();
}
}
所以Request
需要包含User-Id
现在的问题是,我们如何让 OkHttp 从 Jersey 得到这个 User-Id?
发送到RequestInterceptor时需要同步。 我没有看到将东西注入 RequestInterceptor 的简单方法 因为它是 Okhttp。问题是 Jersey 是 Java 1900-2000,其中 Retrofit 和 OkHttp 就像 Java 2015+
所以在一个地方做到这一点的唯一方法 User-Id
是使用 RequestInterceptor
它是一个接口,必须共享,所以 User-Id 必须来自 parameter
内部的 intercept
函数,而不是来自构造函数。
该参数需要有 User-Id
这样我们就不必做像 ThreadLocal 或同步这样令人讨厌的事情。
更新:我做了一个示例项目:
https://github.com/andrewarrow/web-wash
如果您查看此文件:
目标是能够替代:
userClient.getSomething(user_id)
和
userClient.getSomething()
并让 userClient auto-magically 在线程中获取 user_id 安全的方式。请记住:
https://github.com/andrewarrow/web-wash/blob/master/src/team/higher/web/client/UserClient.kt
@GET("user/test")
fun getSomething(@Header("User-Id") id: String): String
将使用@Header 中的 id 导致 OKHttp 和 Retrofit2 生成一个 URL 连接并将该 id 放在该 http GET 的 http header 中 到 api:
User-Id must somehome come from parameter inside intercept function not from constructor. That parameter needs to have User-Id so we don't have to do nasty things like ThreadLocal, or synchronize.
使用代理可以解决这个问题。使用 Jersey,您可以为用户 ID 创建一个小包装器,然后让 Jersey 代理它。我们可以使用 javax.inject.Provider
来在拦截器中延迟检索用户。当我们这样做时,用户将被绑定到请求的上下文(这是有保证的,我们不需要担心管理我们自己的 ThreadLocals 或任何东西)。
public class UserIdInterceptor implements Interceptor {
private final Provider<User> userProvider;
UserIdInterceptor(Provider<User> userProvider) {
this.userProvider = userProvider;
}
@Override
public Response intercept(Chain chain) throws IOException {
final User user = userProvider.get();
if (user.isValid()) {
return chain.proceed(chain.request().newBuilder()
.addHeader("User-Id", userProvider.get().getName())
.build());
} else {
return chain.proceed(chain.request());
}
}
}
我们要做的是使用 Jersey Factory
在请求范围内创建 User
。这样我们就可以为每个请求创建一个带有 cookie 的新用户。
public class UserFactory implements Factory<User> {
@Inject
private Provider<ContainerRequest> request;
@Override
public User provide() {
Cookie cookie = request.get().getCookies().get("User-Id");
return cookie != null
? new User(cookie.getValue())
: new User(null);
}
}
我们还将为 Retrofit
创建一个工厂。这样我们就可以将 Provider<User>
注入到工厂中,并在构造它时将其传递给拦截器。
public class RetrofitFactory implements Factory<Retrofit> {
private final Retrofit retrofit;
@Inject
private RetrofitFactory(Provider<User> userProvider,
BaseUrlProvider urlProvider) {
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new UserIdInterceptor(userProvider))
.build();
this.retrofit = new Retrofit.Builder()
.baseUrl(urlProvider.baseUrl)
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build();
}
@Override
public Retrofit provide() {
return this.retrofit;
}
}
然后将它们绑定在一起 AbstractBinder
@Override
public void configure() {
bindFactory(RetrofitFactory.class)
.to(Retrofit.class)
.in(Singleton.class);
bindFactory(UserFactory.class)
.to(User.class)
.in(RequestScoped.class)
.proxy(true);
bindFactory(ClientFactories.MessageClientFactory.class)
.to(MessageClient.class);
bind(new BaseUrlProvider(this.baseUrl))
.to(BaseUrlProvider.class);
}
我在这个 GitHub Repo
中整理了一个完整的演示