2017-04-03 159 views
1

我有一個調用REST API的Android應用程序。 REST API使用JWT進行保護,當用戶登錄到應用程序時,我得到了android的令牌。該令牌在60分鐘內過期。REST方法 - 在執行其他方法之前執行「常用」方法調用

在我的android應用程序中,我有不同的類,它們總共包含50-60個REST調用。 Android應用程序使用Retrofit連接到這些REST方法。我有一些方法在執行另一個方法後需要工作,這些方法在第一個方法的onResponse方法中。

我有一個特殊的課程,名爲Token,其中setget是來自變量的JWT標記。每個REST調用都會從這個類中獲取令牌。

無論如何,由於REST使用JWT來保護,因此我必須在50分鐘後重新創建令牌。我首先要檢查Token類中的token變量是否即將到期,它有一個特殊的方法whenWillExpire()來告訴我什麼時候令牌將過期。如果是,則再次調用REST API並獲取新的令牌。

這種情況是,因爲我不能告訴哪個REST調用我必須重新創建令牌,所以我必須在any REST調用之前執行此令牌過期檢查,並獲得新令牌(如果過期)選擇的REST方法被調用。舉一個例子,假設我有一個名爲renew()的方法,它檢查令牌並通過執行REST工作從服務器獲取它。執行完renew()之後,應該運行其他REST調用。 renew()應該首先在每個REST呼叫請求上運行。這是這種方法的一個例子。

private void renew(String token) { 

if(token expired){ 
    Gson gson = new GsonBuilder() 
      .setDateFormat("yyyy-MM-dd'T'HH:mm:ss") 
      .create(); 

Retrofit retrofit = new Retrofit.Builder() 
     .baseUrl(RestCommon.URL) 
     .addConverterFactory(GsonConverterFactory.create(gson)) 
     .build(); 

GetNewToken endPoint = retrofit.create(GetNewToken.class); 
Call<ResponseBody> call = endPoint.getNewToken(); 
call.enqueue(new Callback<ResponseBody>() { 
    @Override 
    public void onResponse(Response<ResponseBody> response, Retrofit retrofit) { 
     // Good. Now we have the token. Call other methods here. 
     } 

    @Override 
    public void onFailure(Throwable t) { 
     t.printStackTrace(); 
    } 
}); 
} 
    else 
    { 
     //Not expired. Call other methods. 
    } 
    } 

所以,我怎樣才能確保renew()運行在每一個REST請求,正在執行的請求的方法之前?什麼是最好的設計呢?我喜歡50-60個REST方法,並用不同的名字複製上面的代碼,並在其中添加其他REST調用絕對不是一個好的模式。

+0

這是一個聰明的設計,我通常發生在一個'interface'所有的API調用,我有一個創建'改造改造的1個實例類...'(單),並在所有的呼叫使用它,而且,通常我創建一個類(包裝),其對每個API端點的方法在'interface'並在這方法我稱之爲使用'retrofit'實例調用API,我傳遞'callback'作爲方法匿名內部類,它可能看起來複雜,我可以發佈,如果你的答案想。重點是在包裝方法調用,你可以做所有你想要做的檢查,包括令牌過期,更新呼叫等等 – Yazan

+0

謝謝,請發表回答。但我也有REST調用,它們必須一個接一個地運行。有時候這個鏈條長6-7個方法。這也支持? –

+0

是的,你會在'onResponse()''onFail()'中傳遞匿名內部類作爲'CallBack',你可以做任何你想做的事。我將在幾分鐘內顯示ua示例 – Yazan

回答

0

定義的所有API端點:

public interface MyApi { 
    @GET("item/{id}") 
    Call<Item> getItemById(@Path("id") String id, @Header("Authorization") String auth) 

    @DELETE("item/{id}") 
    Call<String> deleteItemById(@Path("id") String id, @Header("Authorization") String auth) 

    @POST("auth") 
    Call<String> getNewToken(@Field("token") String currentToken);// or other params needed by getNewToken 

    //...etc 
} 

包裝類:

public class MyApiManager{ //?? chose other name if u want :) 

    private MyApi myApi = null; 
    private static MyApiManager instance = null; 

    public MyApiManager getInstance(){ 
     if(instance == null){ 
      instance = new MyApiManager(); 
     } 
     return instance; 
    } 

    private MyApiManager(){ 
     Retrofit retrofit = new Retrofit.Builder() 
     .baseUrl("https://api.host.name") 
     //other setting before build() ... 
     .build(); 

     myApi = retrofit.create(MyApi.class); 
    } 

    //-------- General methods, like check token expired... 

    private boolean checkAndGetNewToken(){ 
     if(token expired){ 
      Thread t = new Thread(new Runnable() { 
       @Override 
       public void run() { 
        //note here using myApi directly to avoid recursive calls for checkAndGetNewToken() 
        Response<String> response = myApi.getNewToken(token).execute(); 
        Token.set(response);//update your token 
       }//run() 
      }); 
      t.start(); 
      t.join(); 
     }//token exp 
     return true; 
    }//checkToken 

    //------ wrapper methods for each API endpoint 

    public Call<Item> getItemById(String id) throws TokenExpiredException{ 
     checkTokenExpiration(); 
     return myApi.getItemById(id, "Authorization:Bearer "+Token.get()); 
    } 

    public Call<Item> deleteItemById(String id) throws TokenExpiredException{ 
     checkTokenExpiration(); 
     return myApi.deleteItemById(id, "Authorization:Bearer "+Token.get()); 
    } 

    public Call<Item> getNewToken(String currentToken) throws TokenExpiredException{ 
     checkTokenExpiration(); 
     return myApi.getNewToken(currentToken); 
    } 
} 

用例:

public class SomeClass { 
    public void someMethod(){ 
     MyApiManager.getInstance().getItem("102").enqueue(new Callback<Item>() { 
      @Override 
      public void onResponse(Response<Item> response, Retrofit retrofit) { 
       callApiStageB(response.getBody().getId()); //? 
      } 

      @Override 
      public void onFailure(Throwable t) { 
       t.printStackTrace(); 
       //show a toast ... 
      } 
     }); 
    } 

    public void callApiStageB(String id){ 
     MyApiManager.getInstance().deleteItemById(id).enqueue(new Callback<String>() { 
      @Override 
      public void onResponse(Response<String> response, Retrofit retrofit) { 
       //....more nested api-calls, or do anything else 
      } 

      @Override 
      public void onFailure(Throwable t) { 
       t.printStackTrace(); 
       //show a toast ... 
      } 
     }); 
    } 
} 

,你可以看到,每個呼叫使用apiManager會首先檢查/請求新的令牌,問題是當需要新的令牌時,處理這個部分,但是如何返回並執行原來的請求(ex getItem())在這一步之後,因爲它應該通過enqueue()CallBack

我建議sol這個(線程和連接())是骯髒的,我認爲它會拋出一些例外,比如NetworkOnMainThread ...希望這有助於你以任何方式:)

相關問題