如何使Okhttp从球衣中获取一个参数并在拦截器中使用



使用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从球衣中获取此用户ID?

发送到requestInterceptor时,我们需要同步它。我看不到将事物注入requestInterceptor的简单方法因为它是okhttp。问题是泽西岛是Java 1900-2000,改造和OKHTTP就像Java 2015

因此,在一个地方使用User-Id的唯一方法是使用RequestInterceptor这是一个接口,必须共享它,因此用户ID必须来自intercept内部的parameter函数,而不是来自构造函数。该参数需要具有User-Id,因此我们不必做诸如threadlocal或同步的讨厌的事情。

更新:我做了一个Samle项目:

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

如果您查看此文件:

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

目标是能够替换:

userClient.getSomething(user_id)

userClient.getSomething()

并让USERCLIENT自动魔法获取线程中的user_id安全的方式。并记住:

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

@GET("user/test")
fun getSomething(@Header("User-Id") id: String): String

将使用@header中的ID,该ID导致OKHTTP和RETROFIT2做一个URL连接并将该ID放在该HTTP的HTTP标头中到API:

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

用户ID必须某个人来自intercept intercept函数,而不是来自构造函数。该参数需要具有用户ID,因此我们不必做诸如ThreadLocal或同步之类的讨厌的事情。

使用代理有一点解决方法。使用泽西岛,您可以做的就是为用户ID创建一个小包装器,然后让Jersey代理。我们可以使用javax.inject.Provider来做到这一点,以懒洋洋地检索拦截器中的用户。当我们执行此操作时,用户将与请求的上下文相关联(可以保证,我们不必担心管理自己的线程销售或其他任何内容)。

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());
        }
    }
}

我们将要做的是使用泽西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

中整理了一个完整的演示

最新更新