2017-06-06 45 views
5

W mojej aplikacji zaimplementowałem Retrofit do wywołania WebServices i używam OkHttp do używania Interceptor i Authenticator. Niektóre żądania wymagają token i mam zaimplementowany interfejs Authenticator do obsługi odświeżania (zgodnie z oficjalnym documentation). Ale mam następujący problem: od czasu do czasu w mojej aplikacji muszę wywołać więcej niż jedno żądanie naraz. Z tego powodu dla jednego z nich będę miał błąd 401.OkHttp i Retrofit, odświeżenie tokena z jednoczesnymi żądaniami

Oto mój kod dla połączeń żądanie:

public static <S> S createServiceAuthentication(Class<S> serviceClass, boolean hasPagination) { 

     final String jwt = JWT.getJWTValue(); //Get jwt value from Realm 

     if (hasPagination) { 
      Gson gson = new GsonBuilder(). 
        registerTypeAdapter(Pagination.class, new PaginationTypeAdapter()).create(); 

      builder = 
        new Retrofit.Builder() 
          .baseUrl(APIConstant.API_URL) 
          .addConverterFactory(GsonConverterFactory.create(gson)); 
     } 

     OkHttpClient.Builder httpClient = 
       new OkHttpClient.Builder(); 

     httpClient.addInterceptor(new AuthenticationInterceptor(jwt)); 
     httpClient.authenticator(new Authenticator() { 
      @Override 
      public Request authenticate(Route route, Response response) throws IOException { 
       if (responseCount(response) >= 2) { 
        // If both the original call and the call with refreshed token failed, 
        // it will probably keep failing, so don't try again. 
        return null; 
       } 

       if (jwt.equals(response.request().header("Authorization"))) { 
        return null; // If we already failed with these credentials, don't retry. 
       } 

       APIRest apiRest = createService(APIRest.class, false); 
       Call<JWTResponse> call = apiRest.refreshToken(new JWTBody(jwt)); 
       try { 
        retrofit2.Response<JWTResponse> refreshTokenResponse = call.execute(); 
        if (refreshTokenResponse.isSuccessful()) { 

         JWT.storeJwt(refreshTokenResponse.body().getJwt()); 

         return response.request().newBuilder() 
           .header(CONTENT_TYPE, APPLICATION_JSON) 
           .header(ACCEPT, APPLICATION) 
           .header(AUTHORIZATION, "Bearer " + refreshTokenResponse.body().getJwt()) 
           .build(); 
        } else { 
         return null; 
        } 
       } catch (IOException e) { 
        return null; 
       } 
      } 
     }); 

     builder.client(httpClient.build()); 
     retrofit = builder.build(); 

     return retrofit.create(serviceClass); 
    } 

    private static int responseCount(Response response) { 
     int result = 1; 
     while ((response = response.priorResponse()) != null) { 
      result++; 
     } 
     return result; 
    } 

Sprawa jest prosta, pierwsza prośba zostanie odświeżona token pomyślnie, ale inni będą powiodła się, ponieważ będą one odśwież token już odświeżony. WebService zwraca błąd 500. Czy istnieje jakieś eleganckie rozwiązanie, aby tego uniknąć?

Dziękujemy!

+0

To może ci pomóc, mam nadzieję, że nie jest za późno https://stackoverflow.com/a/48518733/8187386 –

Odpowiedz

0

Usługa zapisu.

public class TokenService extends Service { 

private static final String TAG = "HelloService"; 

private boolean isRunning = false; 
OkHttpClient client; 
JSONObject jsonObject; 
public static String URL_LOGIN = "http://server.yoursite"; 
String phone_number, password; 

@Override 
public void onCreate() { 
    Log.i(TAG, "Service onCreate"); 
    jsonObject = new JSONObject(); 
    client = new OkHttpClient(); 

    SharedPreferences pref_phone = getSharedPreferences("Full_Phone", MODE_PRIVATE); 
    phone_number = pref_phone.getString("Phone", ""); 

    SharedPreferences pref_password = getSharedPreferences("User_Password", MODE_PRIVATE); 
    password = pref_password.getString("Password", ""); 

    isRunning = true; 
} 

@Override 
public int onStartCommand(Intent intent, int flags, int startId) { 

    Log.i(TAG, "Service onStartCommand"); 
    try { 
     jsonObject.put("phone_number", phone_number); 
     jsonObject.put("password", password); 

    } catch (JSONException e) { 
     e.printStackTrace(); 
    } 
    new Thread(new Runnable() { 
     @Override 
     public void run() { 
      for (; ;) { 
       try { 
        Thread.sleep(1000 * 60 * 2); 
       } catch (Exception e) { 
       } 

       if (isRunning) { 

        AsyncTaskRunner myTask = new AsyncTaskRunner(); 
        myTask.execute(); 

       } else { 

        Log.d("CHECK__", "Check internet connection"); 

       } 
      } 
     } 
    }).start(); 

    return Service.START_STICKY; 
} 

@Override 
public IBinder onBind(Intent arg0) { 
    Log.i(TAG, "Service onBind"); 
    return null; 
} 

@Override 
public void onDestroy() { 

    isRunning = false; 
    Log.i(TAG, "Service onDestroy"); 
} 

String post(String url, JSONObject login) { 
    try { 
     MediaType mediaType = MediaType.parse("application/json"); 
     RequestBody body = RequestBody.create(mediaType, login.toString()); 
     okhttp3.Request request = new okhttp3.Request.Builder() 
       .url(url) 
       .post(body) 
       .build(); 

     okhttp3.Response response = client.newCall(request).execute(); 

     try { 
      return response.body().string(); 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } 
    } catch (Exception e) { 
     e.printStackTrace(); 
    } 
    return ""; 
} 

String response; 

private class AsyncTaskRunner extends AsyncTask<String, String, String> { 

    @Override 
    protected void onPreExecute() { 
    } 

    @Override 
    protected String doInBackground(String... params) { 

     try { 
      response = post(
        URL_LOGIN, jsonObject); 

     } catch (Exception e) { 
      e.printStackTrace(); 
     } 
     return null; 
    } 

    @Override 
    protected void onPostExecute(String result) { 
     Log.d("---OKHTTP---", response); 
    } 
} 

}

+0

Czy rozumiem twoją odpowiedź? Dla mnie jest całkowicie nie na temat. Co więcej, używam retrofitu, tak jak powiedziałem, a nie AyncTask – Guimareshh

0

Jeśli rozumiem problemu, niektóre wnioski są wysyłane podczas token jest aktualizowany, co daje błąd.

Możesz próbować zapobiec wszystkim żądaniom podczas aktualizowania tokena (z obiektem "zsynchronizowanym"), ale nie będzie to dotyczyć już wysłanego żądania.

Ponieważ problem jest trudny do uniknięcia, być może właściwym podejściem jest dobre zachowanie awaryjne. Obsługa błędu otrzymywanego po zgłoszeniu żądania podczas aktualizacji tokena przez ponowne uruchomienie żądania przy użyciu zaktualizowanego tokena na przykład.