2017-06-06 15 views
5

私のアプリケーションでは、WebServicesを呼び出すためにRetrofitを実装しました。また、InterceptorとAuthenticatorを使用するためにOkHttpを使用しています。一部のリクエストにはtokenが必要です。認証コードを入力してください(documentationに従う)。しかし、私は次の問題を抱えています。私のアプリでは、一度に複数のリクエストを呼び出す必要があります。そのため、そのうちの1つでは、401エラーが発生します。ここでOkHttpとRetrofit、並行要求を使用してトークンをリフレッシュ

は、要求のための私のコードで呼び出します。問題は簡単です

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

、最初の要求が正常にトークンを更新しますが、彼らはすでに、リフレッシュトークンをリフレッシュしようとするので、他の人が失敗します。 WebServiceはエラー500を返します。これを避けるための洗練されたソリューションはありますか?

ありがとうございました!

+0

これはあなたに役立つかもしれませんが、遅すぎることはありませんhttps://stackoverflow.com/a/48518733/8187386 –

答えて

0

書き込みサービス。私はあなたの問題を理解していれば、トークンが更新されている間、いくつかの要求が送信され

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

あなたの回答を理解できますか?私の場合、それは完全に対象外です。さらに私は、AyncTaskではなく、私が言ったように改造を使っています – Guimareshh

0

が、これはあなたにエラーが発生します。

トークンが更新されている間に( 'synchronized'オブジェクトを使用して)すべてのリクエストを防止しようとする可能性がありますが、これはすでに送信されたリクエストのケースをカバーしません。

この問題は完全に回避するのが難しいため、ここで正しいアプローチは良いフォールバック動作を持たせることです。たとえば、更新されたトークンを使用して要求を再実行して、トークンの更新中にリクエストを行ったときに発生するエラーを処理します。

関連する問題