如何让OkHttp从Jersey获取参数并在Interceptor中使用?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何让OkHttp从Jersey获取参数并在Interceptor中使用?相关的知识,希望对你有一定的参考价值。

使用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必须有些来自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连接,并将该http的http头中的id放入api:

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

答案

User-Id必须来自拦截函数内部的参数,而不是来自构造函数。该参数需要具有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<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中整理了一个完整的演示

以上是关于如何让OkHttp从Jersey获取参数并在Interceptor中使用?的主要内容,如果未能解决你的问题,请参考以下文章

如何从 jersey2 客户端获取 list<String> 作为响应

Jersey写Restful接口获取参数的问题

如何使用 OkHttp 连接 SPDY 网站?

如何使用 Jersey 获取完整的 REST 请求正文?

使用OkHttp拦截器和Retrofit进行缓存

使用OkHttp拦截器和Retrofit进行缓存