2017-08-30 1 views
0

L'utilisation de Dropwizard + Jersey pour une application Web qu'un utilisateur connecte avec son nom d'utilisateur/mot de passe et un cookie avec son identifiant est créée. Ils renvoient ce cookie d'identifiant utilisateur avec chaque requête.Comment puis-je faire OkHttp obtenir un paramètre de Jersey et utiliser dans Interceptor?

Je peux obtenir ce cookie très bien à Jersey. Le problème est quand je veux impliquer Retrofit2 et 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(); 
    } 
} 

Alors que Request doivent contient User-Id maintenant le problème est, comment pouvons-nous faire OkHttp obtenir ce numéro d'utilisateur de Jersey?

Nous devons le synchroniser lorsque nous l'envoyons à RequestInterceptor. Je ne vois pas un moyen facile d'injecter des choses dans RequestInterceptor parce que c'est Okhttp. Le problème est Jersey sont Java 1900-2000 où Retrofit et OkHttp sont comme Java 2015+

La seule façon de le faire User-Id en un seul endroit est utilise RequestInterceptor Il est une interface et elle doit être partagée si l'utilisateur doit Id somehome viennent de parameter à l'intérieur intercept fonction pas du constructeur. Ce paramètre doit avoir User-Id afin que nous ne devions pas faire de mauvaises choses comme ThreadLocal ou synchroniser.

MISE À JOUR: J'ai fait un projet SAMLE:

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

Si vous regardez ce fichier:

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

L'objectif est d'être en mesure de remplacer:

userClient.getSomething (user_id)

avec

userClient.getSomething()

et ont l'auto-magie userClient obtenir le user_id dans un fil de manière sûre. Et garder à l'esprit:

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

@GET ("utilisateur/test") de getSomething amusant ( @Header ("User-ID") id: String ): String

utilisera l'id dans @Header qui provoque OKHttp et Retrofit2 pour établir une connexion URL et placez cet identifiant dans l'en-tête http de cette http GET à l'api:

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

Répondre

2

L'identifiant d'utilisateur doit provenir du paramètre à l'intérieur de la fonction d'interception et non du constructeur. Ce paramètre doit avoir un User-Id afin que nous n'ayons pas à faire de mauvaises choses comme ThreadLocal, ou à synchroniser.

Il existe un moyen de contourner ce problème en utilisant des proxies.Avec Jersey, ce que vous pouvez faire est de créer un petit wrapper pour l'identifiant de l'utilisateur, puis laissez Jersey le proxy. Nous pouvons le faire avec un javax.inject.Provider pour récupérer paresseusement l'utilisateur dans l'intercepteur. Lorsque nous faisons cela, l'utilisateur sera lié au contexte de la demande (ceci est garanti, nous n'avons pas besoin de nous soucier de gérer nos propres ThreadLocals ou quoi que ce soit).

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

Ce que nous allons faire est d'utiliser un Jersey Factory pour créer le User dans un champ de demande. De cette façon, nous serons en mesure de créer un nouvel utilisateur avec le cookie pour chaque demande. Nous allons également créer une usine pour Retrofit. De cette façon, nous pouvons injecter le Provider<User> dans l'usine et le passer à l'intercepteur lorsque nous le construisons.

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

Et puis le lier tous ensemble dans un 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); 
} 

Je mis en place une démonstration complète dans ce GitHub Repo