【问题标题】:How can I make OkHttp get a parameter out of Jersey and use in Interceptor?如何让 OkHttp 从 Jersey 中获取参数并在 Interceptor 中使用?
【发布时间】:2017-08-30 23:32:34
【问题描述】:

将 Dropwizard+Jersey 用于用户使用其用户名/密码登录的 Web 应用程序,并创建带有用户 ID 的 cookie。他们在每个请求中发回这个用户 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 必须 somehome 来自 parameter 内部 intercept 函数而不是构造函数。 该参数需要有User-Id,这样我们就不必做ThreadLocal之类的讨厌的事情,或者同步。

更新:我做了一个示例项目:

https://github.com/andrewarrow/web-wash

如果您查看此文件:

https://github.com/andrewarrow/web-wash/blob/master/src/team/higher/web/resource/DashboardResource.kt

目标是能够替换:

userClient.getSomething(user_id)

userClient.getSomething()

并让 userClient 在线程中自动神奇地获取 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 标头中 到 api:

https://api.github.com/user/test

【问题讨论】:

    标签: jersey retrofit retrofit2 dropwizard okhttp


    【解决方案1】:

    User-Id 必须 somehome 来自拦截函数内部的参数,而不是来自构造函数。该参数需要有 User-Id,这样我们就不必做 ThreadLocal 之类的讨厌的事情,或者同步。

    使用代理可以解决这个问题。使用 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&lt;User&gt; 注入工厂,并在我们构造它时将其传递给拦截器。

    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中整理了一个完整的演示

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-02-05
      • 2022-11-16
      • 2021-08-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多